提交
This commit is contained in:
@@ -2301,7 +2301,8 @@ str = str.replace(regex, function (match, content, offset, fullString) {
|
|||||||
{ selector: '.tox-tbtn[data-mce-name="edit"]', className: 'tinymce-custom-button-edit' },
|
{ selector: '.tox-tbtn[data-mce-name="edit"]', className: 'tinymce-custom-button-edit' },
|
||||||
{ selector: '.tox-tbtn[data-mce-name="save"]', className: 'tinymce-custom-button-save' },
|
{ selector: '.tox-tbtn[data-mce-name="save"]', className: 'tinymce-custom-button-save' },
|
||||||
{ selector: '.tox-tbtn[data-mce-name="customblue"]', className: 'tinymce-custom-button-blue' },
|
{ selector: '.tox-tbtn[data-mce-name="customblue"]', className: 'tinymce-custom-button-blue' },
|
||||||
{ selector: '.tox-tbtn[data-mce-name="removeblue"]', className: 'tinymce-custom-button-removeblue' }
|
{ selector: '.tox-tbtn[data-mce-name="removeblue"]', className: 'tinymce-custom-button-removeblue' },
|
||||||
|
{ selector: '.tox-tbtn[data-mce-name="selecttemplate"]', className: 'tinymce-custom-button-selecttemplate' }
|
||||||
];
|
];
|
||||||
|
|
||||||
// 遍历每个按钮并为每个按钮添加类
|
// 遍历每个按钮并为每个按钮添加类
|
||||||
|
|||||||
@@ -817,6 +817,9 @@ const en = {
|
|||||||
"tooLarge": "Too Large (>1MB)",
|
"tooLarge": "Too Large (>1MB)",
|
||||||
"error": "Error: {msg}",
|
"error": "Error: {msg}",
|
||||||
"imgLabel": "Img"
|
"imgLabel": "Img"
|
||||||
|
},
|
||||||
|
mailTemplate: {
|
||||||
|
noTemplateTip: 'No templates are available for the current journal. Please select another journal or contact the administrator to configure templates.'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -798,6 +798,10 @@ const zh = {
|
|||||||
"tooLarge": "过大跳过(>1MB)",
|
"tooLarge": "过大跳过(>1MB)",
|
||||||
"error": "失败: {msg}",
|
"error": "失败: {msg}",
|
||||||
"imgLabel": "图"
|
"imgLabel": "图"
|
||||||
|
},
|
||||||
|
mailTemplate: {
|
||||||
|
// 如果已经有 mailTemplate,就只加这一行
|
||||||
|
noTemplateTip: '当前期刊暂无可用模板,请重新选择期刊或联系管理员配置模板。'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import CommonJS from '@/common/js/commonJS'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TinyEditor',
|
name: 'TinyEditor',
|
||||||
props: {
|
props: {
|
||||||
value: { type: String, default: '' },
|
value: { type: String, default: '' },
|
||||||
id: { type: String, default: () => 'tiny-' + +new Date() }
|
id: { type: String, default: () => 'tiny-' + +new Date() },
|
||||||
|
showSelectTemplateButton: { type: Boolean, default: false }
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -114,21 +117,26 @@ autoInlineStyles(htmlContent) {
|
|||||||
return "<!DOCTYPE html>\n" + doc.documentElement.outerHTML;
|
return "<!DOCTYPE html>\n" + doc.documentElement.outerHTML;
|
||||||
},
|
},
|
||||||
initTiny() {
|
initTiny() {
|
||||||
|
const vueInstance = this;
|
||||||
|
const hasTemplateBtn = this.showSelectTemplateButton;
|
||||||
window.tinymce.init({
|
window.tinymce.init({
|
||||||
selector: `#${this.tinymceId}`,
|
selector: `#${this.tinymceId}`,
|
||||||
language: 'zh_CN', // 记得下载语言包,没有就删掉这行
|
language: 'zh_CN', // 记得下载语言包,没有就删掉这行
|
||||||
height: 500,
|
height: 500,
|
||||||
// 核心配置:允许所有 HTML 标签和样式,这对比邮件模板至关重要
|
// 核心配置:允许所有 HTML 标签和样式,这对比邮件模板至关重要
|
||||||
valid_children: '+body[style],+p[style],+div[style]',
|
valid_children: '+body[tr],+body[thead],+body[tbody],+body[table],+body[style],+p[style],+div[style]',
|
||||||
extended_valid_elements: 'style,meta,title',
|
extended_valid_elements: 'style,meta,title',
|
||||||
custom_elements: 'style,meta,title',
|
custom_elements: 'style,meta,title',
|
||||||
verify_html: false, // 关闭 HTML 校验,防止自动删掉你的邮件结构
|
verify_html: false, // 关闭 HTML 校验,防止自动删掉你的邮件结构
|
||||||
|
forced_root_block: '', // 停止自动包裹 <p> 标签
|
||||||
|
|
||||||
// 插件需要包含这些,否则工具栏按钮不会显示
|
// 插件需要包含这些,否则工具栏按钮不会显示
|
||||||
plugins: 'code link image table lists fullscreen directionality codesample preview charmap autolink nonbreaking',
|
plugins: 'code link table lists fullscreen directionality codesample preview charmap autolink nonbreaking',
|
||||||
|
|
||||||
// 按照图片布局精准排序:
|
// 按照图片布局精准排序:
|
||||||
toolbar: 'undo redo | code preview | fontselect fontsizeselect | formatselect | bold italic underline strikethrough | forecolor backcolor | bullist numlist | lineheight outdent indent | alignleft aligncenter alignright alignjustify | image table link | fullscreen',
|
toolbar: hasTemplateBtn
|
||||||
|
? 'undo redo | selectTemplate | code preview | fontselect fontsizeselect | formatselect | bold italic underline strikethrough | forecolor backcolor | bullist numlist | lineheight outdent indent | alignleft aligncenter alignright alignjustify | table link | fullscreen'
|
||||||
|
: 'undo redo | code preview | fontselect fontsizeselect | formatselect | bold italic underline strikethrough | forecolor backcolor | bullist numlist | lineheight outdent indent | alignleft aligncenter alignright alignjustify | table link | fullscreen',
|
||||||
|
|
||||||
// 补充:让字体和字号选择器更有序
|
// 补充:让字体和字号选择器更有序
|
||||||
fontsize_formats: '12px 14px 16px 18px 24px 36px 48px',
|
fontsize_formats: '12px 14px 16px 18px 24px 36px 48px',
|
||||||
@@ -137,14 +145,29 @@ autoInlineStyles(htmlContent) {
|
|||||||
promotion: false,
|
promotion: false,
|
||||||
// 邮件编辑优化:允许在源码中看到完整的 html 结构
|
// 邮件编辑优化:允许在源码中看到完整的 html 结构
|
||||||
fullpage_enabled: true,
|
fullpage_enabled: true,
|
||||||
|
setup(editor) {
|
||||||
|
// 自定义 Select Template 按钮(触发 Vue 事件给父组件,按需显示)
|
||||||
|
if (hasTemplateBtn) {
|
||||||
|
editor.ui.registry.addButton('selectTemplate', {
|
||||||
|
text: 'Select Template ▾',
|
||||||
|
onAction() {
|
||||||
|
vueInstance.$emit('onSelectTemplate');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一给自定义按钮加样式(无按钮时不会产生影响)
|
||||||
|
CommonJS.inTinymceButtonClass();
|
||||||
|
},
|
||||||
init_instance_callback: (editor) => {
|
init_instance_callback: (editor) => {
|
||||||
this.editor = editor;
|
this.editor = editor;
|
||||||
if (this.value) {
|
if (this.value) {
|
||||||
editor.setContent(this.value);
|
editor.setContent(this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听内容变化
|
// 监听内容变化
|
||||||
editor.on('NodeChange Change KeyUp SetContent', () => {
|
editor.on('NodeChange Change KeyUp SetContent', () => {
|
||||||
this.$emit('input', editor.getContent());
|
vueInstance.$emit('input', editor.getContent());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
274
src/components/page/components/email/TemplateSelectorDialog.vue
Normal file
274
src/components/page/components/email/TemplateSelectorDialog.vue
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="Template Selection"
|
||||||
|
:visible.sync="visible"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
width="90%"
|
||||||
|
top="5vh"
|
||||||
|
destroy-on-close
|
||||||
|
:before-close="handleClose"
|
||||||
|
custom-class="template-modal"
|
||||||
|
>
|
||||||
|
<div class="template-wrapper">
|
||||||
|
<div class="selection-panel">
|
||||||
|
|
||||||
|
<el-tabs v-model="activeStep">
|
||||||
|
<el-tab-pane label="1.Choose Style" name="style">
|
||||||
|
<div class="card-grid">
|
||||||
|
<div
|
||||||
|
v-for="(item,index) in headerStyles" :key="item.id"
|
||||||
|
:class="['card-item', { active: selectedHeaderId === item.id }]"
|
||||||
|
@click="selectedHeaderId = item.id"
|
||||||
|
>
|
||||||
|
<div class="card-img" @click.stop="selectedHeaderId = item.id">
|
||||||
|
<div v-html="item.htmlHeader+item.htmlFooter" style="zoom: 0.15;pointer-events: none; user-select: none;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="card-title"><span>{{ index+1 }}. </span>{{ item.name }}</p>
|
||||||
|
<p
|
||||||
|
class="card-desc"
|
||||||
|
:class="{ expanded: expandedHeaderId === item.id }"
|
||||||
|
@click.stop="toggleDesc(item.id)"
|
||||||
|
>
|
||||||
|
{{ item.description }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="2.Choose Template" name="content">
|
||||||
|
<div style="margin-bottom: 15px; padding: 0 5px;">
|
||||||
|
<p style="font-size: 12px; color: #999; margin-bottom: 5px;">Journal:</p>
|
||||||
|
<el-select
|
||||||
|
v-model="selectedJournalId"
|
||||||
|
placeholder="All Journals"
|
||||||
|
clearable
|
||||||
|
size="small"
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleJournalChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in journalOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="card-grid"
|
||||||
|
v-loading="contentLoading"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(item,index) in contentTemplates" :key="item.id"
|
||||||
|
:class="['card-item', { active: selectedContentId === item.id }]"
|
||||||
|
@click="selectedContentId = item.id"
|
||||||
|
>
|
||||||
|
|
||||||
|
<p class="card-title" style="text-align: left;margin-top: 0;"><span>{{ index+1 }}. </span>{{ item.name }}<br><span style="color:#888 ;">{{ item.subject }}</span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="preview-panel">
|
||||||
|
<div class="preview-label">LIVE PREVIEW</div>
|
||||||
|
<div class="preview-container">
|
||||||
|
<div class="mail-render-box" v-html="combinedHtml"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<div class="footer-info">
|
||||||
|
Selected: <strong>{{ currentSelectionText }}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<el-button @click="handleClose" style="">Cancel</el-button>
|
||||||
|
<el-button type="primary" icon="el-icon-check" @click="submit">Apply Template</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
visible: Boolean
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeStep: 'style',
|
||||||
|
// 期刊过滤
|
||||||
|
journalOptions: [],
|
||||||
|
selectedJournalId: null,
|
||||||
|
selectedHeaderId: null,
|
||||||
|
selectedContentId: null,
|
||||||
|
expandedHeaderId: null,
|
||||||
|
// 头部样式列表(通过接口获取)
|
||||||
|
headerStyles: [],
|
||||||
|
contentTemplates: [],
|
||||||
|
contentLoading: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchJournals();
|
||||||
|
this.fetchHeaderStyles();
|
||||||
|
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 【关键】拼接 HTML
|
||||||
|
combinedHtml() {
|
||||||
|
const header = this.headerStyles.find(h => h.id === this.selectedHeaderId);
|
||||||
|
const content = this.contentTemplates.find(c => c.id === this.selectedContentId);
|
||||||
|
|
||||||
|
// 没有可用模板时,右侧显示提示文案(走国际化)
|
||||||
|
if (!header || !content) {
|
||||||
|
const msg = this.$t('mailTemplate.noTemplateTip');
|
||||||
|
return `<div style="padding: 40px; text-align: center; color: #999; font-size: 14px;">
|
||||||
|
${msg}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回完整的拼接代码,使用内联样式以兼容邮件
|
||||||
|
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);
|
||||||
|
return `${h ? h.name : ''} + ${c ? c.name : ''}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 获取期刊列表用于过滤模板
|
||||||
|
async fetchJournals() {
|
||||||
|
try {
|
||||||
|
const res = await this.$api.post('api/Journal/getAllJournal', {});
|
||||||
|
if (!res || res.code !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.journalOptions = res.data.journals.map(j => ({
|
||||||
|
id: j.journal_id,
|
||||||
|
label:j.title,
|
||||||
|
}));
|
||||||
|
if (this.journalOptions.length && !this.selectedJournalId) {
|
||||||
|
this.selectedJournalId = this.journalOptions[0].id;
|
||||||
|
|
||||||
|
this.fetchContentTemplates();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 静默失败
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleDesc(id) {
|
||||||
|
this.expandedHeaderId = this.expandedHeaderId === id ? null : id;
|
||||||
|
},
|
||||||
|
async handleJournalChange() {
|
||||||
|
// 切换期刊时重新拉取样式和模板(按需可改为带 journal_id 过滤)
|
||||||
|
this.contentTemplates=[]
|
||||||
|
await this.fetchContentTemplates();
|
||||||
|
if (this.contentTemplates.length) {
|
||||||
|
this.selectedContentId = this.contentTemplates[0].id;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 调用接口获取头部样式列表
|
||||||
|
async fetchHeaderStyles() {
|
||||||
|
try {
|
||||||
|
const res = await this.$api.post('api/mail_template/listStyles', {
|
||||||
|
journal_id: this.selectedJournalId
|
||||||
|
});
|
||||||
|
if (!res || res.code !== 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后端返回字段:style_id, name, header_html, footer_html
|
||||||
|
|
||||||
|
this.headerStyles = res.data.list.map(item => ({
|
||||||
|
id: item.style_id,
|
||||||
|
name: item.name,
|
||||||
|
description:item.description || '',
|
||||||
|
htmlHeader: item.header_html || '',
|
||||||
|
htmlFooter: item.footer_html || ''
|
||||||
|
}));
|
||||||
|
console.log("🚀 ~ headerStyles:", this.headerStyles);
|
||||||
|
// 默认选中第一条
|
||||||
|
if (this.headerStyles.length && !this.selectedHeaderId) {
|
||||||
|
this.selectedHeaderId = this.headerStyles[0].id;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 静默失败,避免打断用户流程
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 调用接口获取内容模板列表
|
||||||
|
async fetchContentTemplates() {
|
||||||
|
try {
|
||||||
|
this.contentLoading = true;
|
||||||
|
const res = await this.$api.post('api/mail_template/listTemplates', {
|
||||||
|
journal_id: this.selectedJournalId
|
||||||
|
});
|
||||||
|
if (!res || res.code !== 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后端返回字段:template_id, title, body_html 等
|
||||||
|
this.contentTemplates = res.data.list.map(item => ({
|
||||||
|
id: item.template_id,
|
||||||
|
name: item.title,
|
||||||
|
subject: item.subject,
|
||||||
|
bodyHtml: item.body_html || ''
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 默认选中第一条
|
||||||
|
if (this.contentTemplates.length && !this.selectedContentId) {
|
||||||
|
this.selectedContentId = this.contentTemplates[0].id;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 静默失败
|
||||||
|
} finally {
|
||||||
|
this.contentLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleClose() {
|
||||||
|
this.$emit('update:visible', false);
|
||||||
|
},
|
||||||
|
submit() {
|
||||||
|
// 将拼接好的 HTML 抛给父组件
|
||||||
|
this.$emit('confirm', this.combinedHtml);
|
||||||
|
this.handleClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.template-wrapper { display: flex; height: 70vh; gap: 20px; }
|
||||||
|
.selection-panel { width: 260px; border-right: 1px solid #eee; padding-right: 10px; overflow-y: auto; }
|
||||||
|
.journal-filter-form { padding-right: 8px; }
|
||||||
|
.preview-panel { flex: 1; background: #f4f6f8; padding: 20px; display: flex; flex-direction: column; }
|
||||||
|
.preview-label { font-weight: bold; color: #999; font-size: 12px; margin-bottom: 10px; }
|
||||||
|
.preview-container { background: #fff; flex: 1; border-radius: 4px; overflow-y: auto; box-shadow: 0 4px 12px rgba(0,0,0,0.05); }
|
||||||
|
|
||||||
|
.card-grid { display: flex; flex-direction: column; gap: 12px; margin-top: 15px; }
|
||||||
|
.card-item { border: 1px solid #ddd; border-radius: 8px; padding: 8px; cursor: pointer; transition: 0.3s; }
|
||||||
|
.card-item.active { border-color: #6366f1; background: #f5f7ff; outline: 1px solid #6366f1; }
|
||||||
|
.card-img { width: 100%; height: auto; border-radius: 4px; }
|
||||||
|
.card-title { font-size: 13px; margin: 8px 0 0; text-align: center; color: #666; }
|
||||||
|
.card-desc {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
margin-top: 4px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.card-desc.expanded {
|
||||||
|
white-space: normal;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer { display: flex; justify-content: space-between; align-items: center; width: 100%; }
|
||||||
|
.footer-info { color: #666; font-size: 14px; }
|
||||||
|
</style>
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mail-list-panel" :style="{ width: listWidth + 'px' }" v-if="selectedAccount">
|
<div class="mail-list-panel" :style="{ width: listWidth + 'px' }" v-if="selectedAccount">
|
||||||
<div class="panel-header">
|
<!-- <div class="panel-header">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchKeyword"
|
v-model="searchKeyword"
|
||||||
prefix-icon="el-icon-search"
|
prefix-icon="el-icon-search"
|
||||||
@@ -48,8 +48,8 @@
|
|||||||
clearable
|
clearable
|
||||||
@change="handleSearch"
|
@change="handleSearch"
|
||||||
></el-input>
|
></el-input>
|
||||||
<!-- <el-button icon="el-icon-refresh" circle :loading="syncLoading" @click="handleSyncInbox" style="margin-left: 10px;"></el-button> -->
|
<el-button icon="el-icon-refresh" circle :loading="syncLoading" @click="handleSyncInbox" style="margin-left: 10px;"></el-button>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<div ref="listScrollArea" class="list-scroll-area" @scroll="onListScroll">
|
<div ref="listScrollArea" class="list-scroll-area" @scroll="onListScroll">
|
||||||
<template v-if="displayList.length > 0">
|
<template v-if="displayList.length > 0">
|
||||||
|
|||||||
@@ -106,6 +106,8 @@
|
|||||||
:source-content.sync="sourceContent"
|
:source-content.sync="sourceContent"
|
||||||
:source-rows="16"
|
:source-rows="16"
|
||||||
:source-placeholder="$t('mailboxSend.sourcePlaceholder')"
|
:source-placeholder="$t('mailboxSend.sourcePlaceholder')"
|
||||||
|
:show-select-template-button="true"
|
||||||
|
@onSelectTemplate="showTemplateDialog = true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="mail-footer-bar" :style="{ left: footerBarLeft }">
|
<div class="mail-footer-bar" :style="{ left: footerBarLeft }">
|
||||||
@@ -153,31 +155,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 选择模板 -->
|
<template-selector-dialog v-if="showTemplateDialog"
|
||||||
<el-dialog :title="$t('mailboxSend.selectTemplate')" :visible.sync="Templatebox" width="620px" :close-on-click-modal="false">
|
:visible.sync="showTemplateDialog"
|
||||||
<el-form ref="Tempform" :model="TempForm" label-width="225px">
|
@confirm="handleTemplateApply"
|
||||||
<el-form-item :label="$t('mailboxSend.chooseTemplate')">
|
/>
|
||||||
<el-select v-model="TempForm.board" :placeholder="$t('mailboxSend.chooseTemplatePlaceholder')" @change="select_tem($event)" style="width: 220px;">
|
|
||||||
<el-option :key="0" :label="$t('mailboxSend.none') " :value="0"></el-option>
|
|
||||||
<el-option v-for="item in fol_low" :key="item.value" :label="item.label" :value="item.value">
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('mailboxSend.previewTemplate')">
|
|
||||||
<img src="../../assets/img/img.jpg" alt="" style="width: 250px;">
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<span slot="footer" class="dialog-footer">
|
|
||||||
<el-button @click="Templatebox = false">{{ $t('mailboxSend.cancel') }}</el-button>
|
|
||||||
<el-button type="primary" @click="saveTemplate">{{ $t('mailboxSend.save') }}</el-button>
|
|
||||||
</span>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import emailCkeditor from '@/components/page/components/email/CkeditorMail.vue'
|
import emailCkeditor from '@/components/page/components/email/CkeditorMail.vue'
|
||||||
|
import TemplateSelectorDialog from '@/components/page/components/email/TemplateSelectorDialog.vue'
|
||||||
import 'multi-items-input'
|
import 'multi-items-input'
|
||||||
import 'multi-items-input/dist/multi-items-input.css'
|
import 'multi-items-input/dist/multi-items-input.css'
|
||||||
import bus from '../common/bus'
|
import bus from '../common/bus'
|
||||||
@@ -211,7 +198,7 @@ import emailCkeditor from '@/components/page/components/email/CkeditorMail.vue'
|
|||||||
},
|
},
|
||||||
LibrForm: {},
|
LibrForm: {},
|
||||||
LibrarySelection: [],
|
LibrarySelection: [],
|
||||||
Templatebox: false,
|
showTemplateDialog: false,
|
||||||
Librarybox: false,
|
Librarybox: false,
|
||||||
link_TotalLibry: 0,
|
link_TotalLibry: 0,
|
||||||
isSourceMode: false,
|
isSourceMode: false,
|
||||||
@@ -232,6 +219,7 @@ import emailCkeditor from '@/components/page/components/email/CkeditorMail.vue'
|
|||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
emailCkeditor,
|
emailCkeditor,
|
||||||
|
TemplateSelectorDialog,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
footerBarLeft() {
|
footerBarLeft() {
|
||||||
@@ -259,6 +247,16 @@ import emailCkeditor from '@/components/page/components/email/CkeditorMail.vue'
|
|||||||
bus.$off('collapse-content');
|
bus.$off('collapse-content');
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handleTemplateApply(htmlContent) {
|
||||||
|
// 假设你使用的是 TinyMCE
|
||||||
|
if (window.tinymce && window.tinymce.activeEditor) {
|
||||||
|
// 建议:如果你想保留已有内容,用 insertContent;
|
||||||
|
// 如果想彻底更换模板,用 setContent。
|
||||||
|
window.tinymce.activeEditor.setContent(htmlContent);
|
||||||
|
|
||||||
|
this.$message.success('Template applied successfully!');
|
||||||
|
}
|
||||||
|
},
|
||||||
// 切换富文本 / 源代码编辑模式(源码用 sourceContent 保留完整 HTML,可自由来回切换)
|
// 切换富文本 / 源代码编辑模式(源码用 sourceContent 保留完整 HTML,可自由来回切换)
|
||||||
toggleSourceMode() {
|
toggleSourceMode() {
|
||||||
if (this.isSourceMode) {
|
if (this.isSourceMode) {
|
||||||
@@ -573,19 +571,7 @@ import emailCkeditor from '@/components/page/components/email/CkeditorMail.vue'
|
|||||||
this.getLibary();
|
this.getLibary();
|
||||||
},
|
},
|
||||||
|
|
||||||
// 模板选择-弹出框
|
|
||||||
handleSetMoudle() {
|
|
||||||
this.Templatebox = true
|
|
||||||
},
|
|
||||||
// 保存模板
|
|
||||||
saveTemplate() {
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
// 下拉换模板预览
|
|
||||||
select_tem(e) {
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,11 +29,13 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item prop="header_html" :label="$t('mailboxStyleDetail.headerHtml')">
|
<el-form-item prop="header_html" :label="$t('mailboxStyleDetail.headerHtml')">
|
||||||
<CkeditorMail v-model="form.header_html" />
|
<el-input type="textarea" v-model="form.header_html" rows="10" :placeholder="$t('mailboxStyleDetail.headerHtmlPlaceholder')"></el-input>
|
||||||
|
<!-- <CkeditorMail v-model="form.header_html" /> -->
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item prop="footer_html" :label="$t('mailboxStyleDetail.footerHtml')">
|
<el-form-item prop="footer_html" :label="$t('mailboxStyleDetail.footerHtml')">
|
||||||
<CkeditorMail v-model="form.footer_html" />
|
<!-- <CkeditorMail v-model="form.footer_html" /> -->
|
||||||
|
<el-input type="textarea" v-model="form.footer_html" rows="10" :placeholder="$t('mailboxStyleDetail.footerHtmlPlaceholder')"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user