自动化推广【约稿】
This commit is contained in:
892
src/components/page/autoPromotionLogs.vue
Normal file
892
src/components/page/autoPromotionLogs.vue
Normal file
@@ -0,0 +1,892 @@
|
||||
<template>
|
||||
<div class="auto-promo-container">
|
||||
<div class="crumbs">
|
||||
<div class="page-header">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><i class="el-icon-s-promotion"></i> {{ $t('autoPromotion.title') }}</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>
|
||||
{{ $t('autoPromotionLogs.detail') }}
|
||||
</el-breadcrumb-item>
|
||||
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<el-card shadow="never" class="journal-header-card">
|
||||
<div class="config-bar">
|
||||
<div class="left">
|
||||
<span class="label">{{ $t('autoPromotion.journal') }} : </span>
|
||||
{{currentJournalName}}
|
||||
|
||||
<template v-if="config.initialized">
|
||||
<el-tag type="success" size="small" effect="plain" style="margin-left: 10px">
|
||||
<i class="el-icon-circle-check"></i> {{ $t('autoPromotionLogs.configured') }}
|
||||
</el-tag>
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
style="margin-left: 10px"
|
||||
@click="openWizardDialog"
|
||||
>
|
||||
<i class="el-icon-edit"></i>
|
||||
{{
|
||||
config.initialized
|
||||
? $t('autoPromotionLogs.editConfig')
|
||||
: $t('autoPromotionLogs.startConfig')
|
||||
}}
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
|
||||
<el-tag v-else type="info" size="small" effect="plain" style="margin-left: 10px">
|
||||
<i class="el-icon-info"></i> {{ $t('autoPromotionLogs.notConfigured') }}
|
||||
</el-tag>
|
||||
|
||||
</div>
|
||||
<div class="right" v-if="config.initialized">
|
||||
<el-button size="small" icon="el-icon-document" @click="openLogs">
|
||||
{{ $t('autoPromotion.logs') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<div v-loading="loading" class="main-body">
|
||||
<el-card v-if="!config.initialized" shadow="never" class="wizard-card">
|
||||
<auto-promotion-wizard
|
||||
mode="inline"
|
||||
:config="config"
|
||||
:wizardStartDate.sync="wizardStartDate"
|
||||
:currentJournalName="currentJournalName"
|
||||
:selectedTemplateThumbHtml="selectedTemplateThumbHtml"
|
||||
:selectedTemplateName="selectedTemplateName"
|
||||
:selectedStyleName="selectedStyleName"
|
||||
:saving="saving"
|
||||
:title="$t('autoPromotion.title')"
|
||||
@open-template-selector="showTemplateDialog = true"
|
||||
@confirm="completeInitialization"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<div v-else class="manage-mode" style="display: none;">
|
||||
|
||||
|
||||
<el-card shadow="never" class="list-card">
|
||||
<div class="list-header">
|
||||
<el-input
|
||||
v-model="query.keyword"
|
||||
size="small"
|
||||
clearable
|
||||
style="width: 300px"
|
||||
:placeholder="$t('autoPromotionLogs.searchPlaceholder')"
|
||||
@keyup.enter.native="fetchList"
|
||||
>
|
||||
|
||||
</el-input>
|
||||
<el-button type="primary" size="small" icon="el-icon-search" style="margin-left: 20px;" @click="fetchList">{{ $t('autoPromotionLogs.searchBtn') }}</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="list" border stripe size="small" class="custom-table">
|
||||
<el-table-column type="index" :label="$t('autoPromotionLogs.index')" width="70" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('autoPromotionLogs.expertInfo')" min-width="180">
|
||||
<template slot-scope="scope">
|
||||
<div class="user-info">
|
||||
<div class="name">{{ scope.row.name }}</div>
|
||||
<div class="email">{{ scope.row.email }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('autoPromotionLogs.templateStyle')" min-width="200">
|
||||
<template slot-scope="scope">
|
||||
<div class="template-info-cell">
|
||||
<div class="text-content">
|
||||
<div class="tpl-main">{{ getTemplateName(scope.row.template_id) }}</div>
|
||||
<div class="tpl-sub">{{ $t('autoPromotionLogs.stylePrefix') }}: {{ getStyleDisplay(scope.row) }}</div>
|
||||
</div>
|
||||
<el-button
|
||||
type="text"
|
||||
icon="el-icon-edit-outline"
|
||||
class="edit-icon-btn"
|
||||
@click="handleEditRowTemplate(scope.row)"
|
||||
></el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="run_at" :label="$t('autoPromotionLogs.runAt')" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span class="time-cell">
|
||||
<i class="el-icon-time"></i> {{ scope.row.run_at }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('autoPromotionLogs.status')" width="120">
|
||||
<template slot-scope="scope">
|
||||
<span :class="['status-badge', scope.row.paused ? 'is-paused' : 'is-waiting']">
|
||||
{{ scope.row.paused ? $t('autoPromotionLogs.paused') : $t('autoPromotionLogs.toRun') }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('autoPromotionLogs.operation')" width="150" align="center">
|
||||
<template slot-scope="scope">
|
||||
<div class="action-btns">
|
||||
<el-tooltip :content="$t('autoPromotionLogs.preview')" placement="top">
|
||||
<el-button type="text" icon="el-icon-view" @click="previewRow(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip :content="scope.row.paused ? $t('autoPromotionLogs.enable') : $t('autoPromotionLogs.pause')" placement="top">
|
||||
<el-button
|
||||
type="text"
|
||||
:icon="scope.row.paused ? 'el-icon-video-play' : 'el-icon-video-pause'"
|
||||
@click="togglePause(scope.row)"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
|
||||
<el-button
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
class="delete-btn"
|
||||
@click="deleteRow(scope.row)"
|
||||
></el-button>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
background
|
||||
layout="total, prev, pager, next"
|
||||
:current-page="query.pageIndex"
|
||||
:page-size="query.pageSize"
|
||||
:total="total"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<auto-promotion-wizard
|
||||
mode="dialog"
|
||||
:visible.sync="showWizardDialog"
|
||||
:config="config"
|
||||
:wizardStartDate.sync="wizardStartDate"
|
||||
:currentJournalName="currentJournalName"
|
||||
:selectedTemplateThumbHtml="selectedTemplateThumbHtml"
|
||||
:selectedTemplateName="selectedTemplateName"
|
||||
:selectedStyleName="selectedStyleName"
|
||||
:saving="saving"
|
||||
:title="$t('autoPromotion.title')"
|
||||
@open-template-selector="showTemplateDialog = true"
|
||||
@cancel="showWizardDialog = false"
|
||||
@confirm="completeInitialization"
|
||||
/>
|
||||
|
||||
<template-selector-dialog
|
||||
v-if="showTemplateDialog"
|
||||
:visible.sync="showTemplateDialog"
|
||||
:journalId="selectedJournalId"
|
||||
:journalLabel="currentJournalName"
|
||||
:initial-style-id="templateDialogInitialStyleId"
|
||||
:initial-template-id="templateDialogInitialTemplateId"
|
||||
:return-source="'autoPromotion'"
|
||||
@confirm="handleTemplateApply"
|
||||
@close-all-dialogs="closeAllDialogs"
|
||||
/>
|
||||
<el-dialog :title="$t('autoPromotionLogs.previewEditTitle')" :visible.sync="showPreviewDialog" width="1200px" top="5vh">
|
||||
<div class="mail-edit-wrapper" v-if="previewForm">
|
||||
<el-form label-width="80px" size="small">
|
||||
<el-form-item :label="$t('autoPromotionLogs.receiver')">
|
||||
<el-input v-model="previewForm.email" disabled :placeholder="$t('autoPromotionLogs.receiverImmutablePlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('autoPromotionLogs.subject')">
|
||||
<el-input v-model="previewForm.subject" :placeholder="$t('autoPromotionLogs.subjectPlaceholder')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('autoPromotionLogs.runAt')">
|
||||
<el-date-picker
|
||||
v-model="previewForm.run_at"
|
||||
type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
:placeholder="$t('autoPromotionLogs.runAtPlaceholder')"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<CkeditorMail v-model="previewForm.content" />
|
||||
|
||||
</el-form>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<el-button size="small" @click="showPreviewDialog = false">{{ $t('autoPromotionLogs.cancel') }}</el-button>
|
||||
<el-button size="small" type="primary" @click="saveMailContent" :loading="saving">{{ $t('autoPromotionLogs.confirmEdit') }}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CkeditorMail from '@/components/page/components/email/CkeditorMail.vue';
|
||||
import TemplateSelectorDialog from '@/components/page/components/email/TemplateSelectorDialog.vue'
|
||||
import AutoPromotionWizard from '@/components/page/components/autoPromotion/AutoPromotionWizard.vue'
|
||||
// 这里假设你已经定义了 API 地址
|
||||
const API = {
|
||||
getAllJournal: 'api/email_client/getPromotionJournalList',
|
||||
listTemplates: 'api/mail_template/listTemplates',
|
||||
getConfig: 'api/auto_promotion/getConfig',
|
||||
saveConfig: 'api/email_client/setDefaultPromotion',
|
||||
list: 'api/auto_promotion/getList',
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'autoPromotion',
|
||||
components: { TemplateSelectorDialog, AutoPromotionWizard, CkeditorMail },
|
||||
data() {
|
||||
return {
|
||||
// 基础数据
|
||||
journalOptions: [],
|
||||
selectedJournalId: '',
|
||||
templateOptions: [],
|
||||
|
||||
// 配置对象
|
||||
config: {
|
||||
initialized: false, // 核心:控制向导与列表的显示
|
||||
enabled: false,
|
||||
defaultTemplateId: '',
|
||||
defaultTime: '09:00:00'
|
||||
},
|
||||
|
||||
// 向导控制
|
||||
wizardStep: 0,
|
||||
wizardStartDate: '',
|
||||
showTemplateDialog: false,
|
||||
selectedTemplateThumbHtml: '',
|
||||
selectedTemplateName: '',
|
||||
selectedStyleName: '',
|
||||
|
||||
// 列表数据
|
||||
query: { keyword: '', pageIndex: 1, pageSize: 15 },
|
||||
list: [],
|
||||
total: 0,
|
||||
|
||||
// 状态控制
|
||||
loading: false,
|
||||
saving: false,
|
||||
currentJournalName: '',
|
||||
// 向导弹窗(用于编辑已初始化配置)
|
||||
showWizardDialog: false,
|
||||
showPreviewDialog: false,
|
||||
currentRow: null,
|
||||
templateDialogInitialStyleId: '',
|
||||
templateDialogInitialTemplateId: '',
|
||||
previewForm: {
|
||||
id: '',
|
||||
email: '',
|
||||
run_at: '',
|
||||
subject: '',
|
||||
content: ''
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
||||
},
|
||||
created() {
|
||||
this.initPage();
|
||||
},
|
||||
methods: {
|
||||
closeAllDialogs() {
|
||||
// 点击“去新增模板”后:关闭当前页面所有可能的弹窗
|
||||
this.showWizardDialog = false;
|
||||
this.showTemplateDialog = false;
|
||||
this.showPreviewDialog = false;
|
||||
this.currentRow = null;
|
||||
},
|
||||
openLogs() {
|
||||
this.$router.push({
|
||||
path: '/autoPromotionLogs',
|
||||
query: { journal_id: this.selectedJournalId || '' }
|
||||
});
|
||||
},
|
||||
// 点击列表“查看”图标
|
||||
previewRow(row) {
|
||||
this.previewForm = {
|
||||
id: row.id,
|
||||
email: row.email,
|
||||
run_at: row.run_at || '',
|
||||
// 假设后端或mock数据里有这些字段,没有则赋初始值
|
||||
subject:
|
||||
row.subject ||
|
||||
this.$t('autoPromotionLogs.mockPromotionSubject', { journal: this.currentJournalName }),
|
||||
content:
|
||||
row.mail_content ||
|
||||
this.$t('autoPromotionLogs.mockPromotionContent', { name: row.name || '' })
|
||||
};
|
||||
this.showPreviewDialog = true;
|
||||
},
|
||||
|
||||
// 保存修改后的邮件内容
|
||||
async saveMailContent() {
|
||||
this.saving = true;
|
||||
try {
|
||||
// 模拟调用 API
|
||||
// await this.$api.post(API.updateMailDetail, this.previewForm);
|
||||
|
||||
// 同步到本地 list 中(Mock 效果)
|
||||
const index = this.list.findIndex(item => item.id === this.previewForm.id);
|
||||
if (index !== -1) {
|
||||
this.$set(this.list[index], 'run_at', this.previewForm.run_at);
|
||||
this.$set(this.list[index], 'subject', this.previewForm.subject);
|
||||
this.$set(this.list[index], 'mail_content', this.previewForm.content);
|
||||
}
|
||||
|
||||
this.$message.success(this.$t('autoPromotionLogs.mailContentSaved'));
|
||||
this.showPreviewDialog = false;
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 修改 handleEditRowTemplate 使其调用你已有的选择器
|
||||
handleEditRowTemplate(row) {
|
||||
this.currentRow = row;
|
||||
this.showTemplateDialog = true;
|
||||
},
|
||||
// 处理表格内点击编辑图标
|
||||
handleEditRowTemplate(row) {
|
||||
this.currentRow = row; // 记录当前正在操作哪一行
|
||||
this.templateDialogInitialStyleId = row && row.style_id ? String(row.style_id) : '';
|
||||
this.templateDialogInitialTemplateId = row && row.template_id ? String(row.template_id) : '';
|
||||
this.showTemplateDialog = true;
|
||||
},
|
||||
|
||||
// 修改原有的 handleTemplateApply 方法,使其兼容向导和表格修改
|
||||
handleTemplateApply(payload) {
|
||||
const templateObj = payload && payload.template ? payload.template : {};
|
||||
const styleObj = payload && payload.style ? payload.style : {};
|
||||
const templateId = templateObj.id || (payload && payload.template_id) || '';
|
||||
const templateName = templateObj.name || '';
|
||||
const styleName = styleObj.name || '';
|
||||
|
||||
if (this.currentRow) {
|
||||
// 说明是修改表格里的某一行
|
||||
this.currentRow.template_id = String(templateId);
|
||||
this.currentRow.template_name = templateName;
|
||||
this.currentRow.style_name = styleName;
|
||||
this.updateRow(this.currentRow); // 提交保存
|
||||
this.currentRow = null; // 清空引用
|
||||
} else {
|
||||
// 说明是向导模式在选择
|
||||
this.config.defaultTemplateId = String(templateId);
|
||||
this.selectedTemplateName = templateName;
|
||||
this.selectedStyleName = styleName;
|
||||
this.selectedTemplateThumbHtml = `<div style="zoom:0.18; pointer-events:none;">${payload.html}</div>`;
|
||||
}
|
||||
this.showTemplateDialog = false;
|
||||
},
|
||||
|
||||
getTemplateName(id) {
|
||||
const found = this.templateOptions.find(t => String(t.template_id) === String(id));
|
||||
return found ? found.title : id || this.$t('autoPromotionLogs.templateNotSelected');
|
||||
},
|
||||
getStyleDisplay(row) {
|
||||
if (!row) return '-';
|
||||
if (row.style_name) return row.style_name;
|
||||
if (row.style_id) return String(row.style_id);
|
||||
return '-';
|
||||
},
|
||||
|
||||
|
||||
async initPage() {
|
||||
var journal_id = (this.$route.query && this.$route.query.journal_id) || '';
|
||||
this.selectedJournalId = String(journal_id);
|
||||
this.loading = true;
|
||||
try {
|
||||
await this.fetchJournalDetail();
|
||||
if (this.config.initialized) {
|
||||
await this.fetchTemplates();
|
||||
await this.fetchList();
|
||||
}
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
_parseJournalDetail(data) {
|
||||
const journalInfo = data.journal || data || {};
|
||||
const tpl = journalInfo.template || data.template || {};
|
||||
const style = journalInfo.style || data.style || {};
|
||||
|
||||
const tplId = String(journalInfo.default_template_id || '0');
|
||||
const styleId = String(journalInfo.default_style_id || '0');
|
||||
|
||||
return {
|
||||
title: journalInfo.title || journalInfo.journal_title || '',
|
||||
templateId: tplId,
|
||||
styleId: styleId,
|
||||
templateName: tpl.name || tpl.title || journalInfo.default_template_name || '',
|
||||
styleName: style.name || style.title || journalInfo.default_style_name || '',
|
||||
html: `${style.header_html || ''}${tpl.body_html || ''}${style.footer_html || ''}`,
|
||||
enabled: String(journalInfo.start_promotion || '0') === '1',
|
||||
initialized: tplId !== '0' && styleId !== '0'
|
||||
};
|
||||
},
|
||||
async fetchJournalDetail() {
|
||||
if (!this.selectedJournalId) {
|
||||
this.config.initialized = false;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var res = await this.$api.post('api/email_client/getPromotionJournalDetail', {
|
||||
journal_id: this.selectedJournalId
|
||||
});
|
||||
var data = (res && res.data) || {};
|
||||
var detail = this._parseJournalDetail(data);
|
||||
var journalInfo = data.journal || data || {};
|
||||
|
||||
this.currentJournalName = detail.title || this.currentJournalName || '';
|
||||
this.config = {
|
||||
initialized: detail.initialized,
|
||||
enabled: detail.enabled, // 回显“是否立即推广”
|
||||
defaultTemplateId: detail.initialized ? detail.templateId : '',
|
||||
defaultStyleId: detail.initialized ? detail.styleId : '',
|
||||
defaultTime: journalInfo.default_time || this.config.defaultTime || ''
|
||||
};
|
||||
|
||||
// 回显默认模板/风格(用于“修改默认配置”)
|
||||
this.selectedTemplateName = detail.initialized ? (detail.templateName || '') : '';
|
||||
this.selectedStyleName = detail.initialized ? (detail.styleName || '') : '';
|
||||
this.selectedTemplateThumbHtml = detail.initialized && detail.html
|
||||
? `<div style="zoom:0.18; pointer-events:none; user-select:none;">${detail.html}</div>`
|
||||
: '';
|
||||
|
||||
// 打开模板选择弹窗时,默认选中当前配置
|
||||
this.templateDialogInitialTemplateId = detail.initialized ? String(detail.templateId || '') : '';
|
||||
this.templateDialogInitialStyleId = detail.initialized ? String(detail.styleId || '') : '';
|
||||
} catch (e) {
|
||||
this.config.initialized = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 打开向导弹窗:用于“修改期刊自动推广配置”
|
||||
openWizardDialog() {
|
||||
this.showWizardDialog = true;
|
||||
this.wizardStep = 0;
|
||||
// 尽量从已加载的 config 里取开始日期(没有就保持空)
|
||||
if (this.config && this.config.start_date) {
|
||||
this.wizardStartDate = this.config.start_date;
|
||||
}
|
||||
},
|
||||
|
||||
// 切换期刊逻辑
|
||||
async handleJournalChange() {
|
||||
this.loading = true;
|
||||
// 切换期刊时:先清空“已选模板/缩略图/风格名”,再判断该期刊是否初始化
|
||||
this.showTemplateDialog = false;
|
||||
this.selectedTemplateThumbHtml = '';
|
||||
this.selectedTemplateName = '';
|
||||
this.selectedStyleName = '';
|
||||
this.config.defaultTemplateId = '';
|
||||
this.wizardStartDate = '';
|
||||
this.wizardStep = 0;
|
||||
try {
|
||||
await this.fetchTemplates(); // 获取对应期刊的模版
|
||||
await this.loadConfig(); // 获取该期刊的初始化状态
|
||||
if (!this.config.initialized) {
|
||||
// 未初始化:确保默认模板为空(保持向导第一步)
|
||||
this.config.defaultTemplateId = '';
|
||||
} else if (this.config.initialized) {
|
||||
await this.fetchList(); // 已初始化的直接加载列表
|
||||
}
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async fetchJournals() {
|
||||
const res = await this.$api.post(API.getAllJournal, {user_id: localStorage.getItem('U_id')});
|
||||
this.journalOptions = res.data.journals || [];
|
||||
},
|
||||
|
||||
async fetchTemplates() {
|
||||
const res = await this.$api.post(API.listTemplates, { journal_id: this.selectedJournalId });
|
||||
this.templateOptions = (res.data.list || []).map(t => ({
|
||||
template_id: t.template_id || t.id,
|
||||
title: t.title || t.name
|
||||
}));
|
||||
},
|
||||
|
||||
async loadConfig() {
|
||||
await this.fetchJournalDetail();
|
||||
},
|
||||
|
||||
// 接收模版选择:兼容向导与列表行编辑
|
||||
handleTemplateApply(payload) {
|
||||
// payload 兼容:可能是 string(html) 或对象
|
||||
const html = payload && typeof payload === 'object' ? payload.html : String(payload || '');
|
||||
const templateId =
|
||||
payload && typeof payload === 'object'
|
||||
? (payload.template_id || payload.id || (payload.template && (payload.template.id || payload.template.template_id)) || '')
|
||||
: '';
|
||||
const styleId =
|
||||
payload && typeof payload === 'object'
|
||||
? (payload.style_id || (payload.style && payload.style.id) || '')
|
||||
: '';
|
||||
const templateName = payload && typeof payload === 'object' && payload.template ? payload.template.name : '';
|
||||
const styleName = payload && typeof payload === 'object' && payload.style ? payload.style.name : '';
|
||||
|
||||
// 必须选到模板(template_id)才允许“下一步”解除置灰
|
||||
if (!templateId) {
|
||||
this.$message.warning(this.$t('autoPromotionLogs.selectTemplateWarning'));
|
||||
return;
|
||||
}
|
||||
if (this.currentRow) {
|
||||
this.currentRow.template_id = String(templateId);
|
||||
this.currentRow.template_name = templateName || this.getTemplateName(templateId);
|
||||
this.currentRow.style_id = styleId ? String(styleId) : this.currentRow.style_id;
|
||||
this.currentRow.style_name = styleName || this.currentRow.style_name || '';
|
||||
this.updateRow(this.currentRow);
|
||||
this.currentRow = null;
|
||||
} else {
|
||||
this.config.defaultTemplateId = String(templateId);
|
||||
this.selectedTemplateName = templateName || '';
|
||||
this.selectedStyleName = styleName || '';
|
||||
// 生成缩略图 HTML(禁用交互,避免点击跳转)
|
||||
this.selectedTemplateThumbHtml = `<div style="zoom:0.18; pointer-events:none; user-select:none;">${html}</div>`;
|
||||
}
|
||||
this.templateDialogInitialStyleId = '';
|
||||
this.templateDialogInitialTemplateId = '';
|
||||
this.showTemplateDialog = false;
|
||||
// this.$message.success(this.$t('autoPromotion.saved') || 'Saved');
|
||||
},
|
||||
|
||||
// 最终提交初始化
|
||||
async completeInitialization() {
|
||||
this.saving = true;
|
||||
try {
|
||||
const payload = {
|
||||
journal_id: String(this.selectedJournalId || ''),
|
||||
default_template_id: String(this.config.defaultTemplateId || ''),
|
||||
default_style_id: String(this.config.defaultStyleId || ''),
|
||||
start_promotion: this.config.enabled ? '1' : '0'
|
||||
};
|
||||
this.config.initialized = true; // 切换到管理模式
|
||||
this.showWizardDialog = false;
|
||||
this.fetchList();
|
||||
await this.$api.post(API.saveConfig, payload);
|
||||
this.$message.success(this.$t('autoPromotionLogs.configUpdated'));
|
||||
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
},
|
||||
|
||||
async saveConfig() {
|
||||
// 用于管理模式下的快捷修改保存
|
||||
await this.$api.post(API.saveConfig, {
|
||||
journal_id: String(this.selectedJournalId || ''),
|
||||
default_template_id: String(this.config.defaultTemplateId || ''),
|
||||
default_style_id: String(this.config.defaultStyleId || ''),
|
||||
start_promotion: this.config.enabled ? '1' : '0'
|
||||
});
|
||||
this.$message.success(this.$t('autoPromotionLogs.configUpdated'));
|
||||
},
|
||||
|
||||
async fetchList() {
|
||||
this.loading = true;
|
||||
try {
|
||||
// 当前接口未联通:直接使用本地假数据
|
||||
const mock = this.generateMockList();
|
||||
this.list = mock.list;
|
||||
this.total = mock.total;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 生成用于页面展示的假数据(mock)
|
||||
generateMockList() {
|
||||
const tpl1 = this.templateOptions && this.templateOptions.length ? String(this.templateOptions[0].template_id) : '1';
|
||||
const tpl2 = this.templateOptions && this.templateOptions.length > 1 ? String(this.templateOptions[1].template_id) : tpl1;
|
||||
|
||||
const baseDate = new Date();
|
||||
// 使用固定的偏移,保证每次打开都有“未来”的时间
|
||||
const d1 = new Date(baseDate.getTime() + 86400000 * 2);
|
||||
const d2 = new Date(baseDate.getTime() + 86400000 * 3);
|
||||
const d3 = new Date(baseDate.getTime() + 86400000 * 4);
|
||||
const d4 = new Date(baseDate.getTime() + 86400000 * 5);
|
||||
const d5 = new Date(baseDate.getTime() + 86400000 * 6);
|
||||
|
||||
const fmt = (d) => {
|
||||
const yyyy = d.getFullYear();
|
||||
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(d.getDate()).padStart(2, '0');
|
||||
return `${yyyy}-${mm}-${dd} 09:00:00`;
|
||||
};
|
||||
|
||||
const mk = (idx, name, email, template_id, style_id, style_name, paused, run_at) => ({
|
||||
id: `mock_${idx}`,
|
||||
name,
|
||||
email,
|
||||
template_id: String(template_id),
|
||||
style_id: String(style_id),
|
||||
style_name: style_name || '',
|
||||
paused: Boolean(paused),
|
||||
run_at
|
||||
});
|
||||
|
||||
const list = [
|
||||
mk(1, '张三', 'zhangsan@example.com', tpl1, 1, '简约风格', false, fmt(d1)),
|
||||
mk(2, '李四', 'lisi@example.com', tpl2, 2, '商务风格', true, fmt(d2)),
|
||||
mk(3, '王五', 'wangwu@example.com', tpl1, 1, '简约风格', false, fmt(d3)),
|
||||
mk(4, '赵六', 'zhaoliu@example.com', tpl2, 3, '学术风格', false, fmt(d4)),
|
||||
mk(5, '钱七', 'qianqi@example.com', tpl1, 2, '商务风格', true, fmt(d5)),
|
||||
];
|
||||
|
||||
return { list, total: list.length };
|
||||
},
|
||||
|
||||
// 模板切换(mock:仅更新本行数据,不做后端请求)
|
||||
updateRow(row) {
|
||||
if (!row) return;
|
||||
row.template_id = row.template_id == null ? '' : String(row.template_id);
|
||||
this.$message.success(this.$t('autoPromotion.saved') || 'Saved');
|
||||
},
|
||||
|
||||
// 暂停/开启(mock:切换 paused)
|
||||
togglePause(row) {
|
||||
if (!row) return;
|
||||
row.paused = !row.paused;
|
||||
if (row.paused) {
|
||||
this.$message({
|
||||
type: 'info',
|
||||
message: this.$t('autoPromotionLogs.pauseSuccess')
|
||||
});
|
||||
} else {
|
||||
this.$message.success(this.$t('autoPromotionLogs.enableSuccess'));
|
||||
}
|
||||
},
|
||||
|
||||
// 删除(mock:从列表移除)
|
||||
deleteRow(row) {
|
||||
if (!row) return;
|
||||
this.list = (this.list || []).filter(item => item !== row);
|
||||
this.total = this.list.length;
|
||||
this.$message.success(this.$t('autoPromotionLogs.deletedSuccess'));
|
||||
},
|
||||
|
||||
handlePageChange(p) {
|
||||
this.query.pageIndex = p;
|
||||
this.fetchList();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.auto-promo-container { background: #f9fafc; min-height: 90%; }
|
||||
.journal-header-card { margin-bottom: 15px; }
|
||||
.config-bar { display: flex; justify-content: space-between; align-items: center; }
|
||||
|
||||
/* 向导样式 */
|
||||
.wizard-card { margin-top: 10px; min-height: 500px; }
|
||||
.wizard-header { padding: 20px 0 40px; border-bottom: 1px solid #f0f2f5; }
|
||||
.wizard-body { padding: 40px 0; }
|
||||
.step-content { text-align: center; }
|
||||
.step-tips { margin-bottom: 30px; }
|
||||
.step-tips i { font-size: 40px; color: #409EFF; margin-bottom: 15px; }
|
||||
|
||||
.template-placeholder {
|
||||
width: 520px;
|
||||
max-width: 100%;
|
||||
border: 2px dashed #dcdfe6;
|
||||
margin: 0 auto;
|
||||
border-radius: 8px;
|
||||
padding: 14px;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.template-placeholder:hover { border-color: #409EFF; color: #409EFF; background: #f5f7fa; }
|
||||
.template-placeholder .inner { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 150px; }
|
||||
.template-placeholder .inner.selected { display: block; min-height: unset; color: #333; }
|
||||
.template-placeholder .inner i { font-size: 30px; }
|
||||
.thumb-box {
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
height: 120px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.thumb-content { transform-origin: top left; }
|
||||
.selected-meta { display: flex; align-items: center; gap: 10px; }
|
||||
.selected-text { flex: 1; min-width: 0; }
|
||||
.tpl-line { color: #606266; line-height: 20px; }
|
||||
.style-line { color: #909399; font-size: 12px; line-height: 18px; margin-top: 2px; }
|
||||
|
||||
.wizard-footer { text-align: center; padding-top: 30px; border-top: 1px solid #f0f2f5; }
|
||||
|
||||
/* 管理模式样式 */
|
||||
.config-card { margin-bottom: 15px; background: #fff; }
|
||||
.list-card { background: #fff; }
|
||||
.list-header { margin-bottom: 15px; }
|
||||
.pagination { margin-top: 20px; text-align: right; }
|
||||
.final-check { margin-top: 15px; line-height: 2; }
|
||||
/* 弹窗容器布局 */
|
||||
.vertical-wizard-container {
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.side-nav {
|
||||
width: 120px;
|
||||
flex-shrink: 0;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.main-form-content {
|
||||
flex: 1;
|
||||
max-height: 60vh; /* 开启滚动条防止内容过长 */
|
||||
overflow-y: auto;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
/* 每一个小节的样式 */
|
||||
.form-section {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0 0 20px 0;
|
||||
font-size: 15px;
|
||||
color: #303133;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section-title i {
|
||||
margin-right: 8px;
|
||||
color: #409EFF;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* 优化后的模板选择框 */
|
||||
.template-placeholder.mini-mode {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
min-height: 60px;
|
||||
background: #f8f9fb;
|
||||
}
|
||||
|
||||
.thumb-box-mini {
|
||||
width: 100px;
|
||||
height: 60px;
|
||||
border: 1px solid #ddd;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.status-confirm-box {
|
||||
padding: 15px;
|
||||
background: #f0f9eb;
|
||||
border-radius: 4px;
|
||||
border: 1px inset #e1f3d8;
|
||||
}
|
||||
|
||||
.selected-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 调整分割线间距 */
|
||||
.el-divider--horizontal {
|
||||
margin: 24px 0;
|
||||
}
|
||||
/* 表格内容排版 */
|
||||
.user-info .name { font-weight: bold; color: #303133; margin-bottom: 2px; }
|
||||
.user-info .email { color: #909399; font-size: 12px; }
|
||||
|
||||
.template-info-cell { display: flex; align-items: center; justify-content: space-between; }
|
||||
.tpl-main { font-weight: 600; color: #409EFF; font-size: 13px; }
|
||||
.tpl-sub { color: #909399; font-size: 12px; margin-top: 2px; }
|
||||
.edit-icon-btn { font-size: 16px; margin-left: 8px; color: #909399; }
|
||||
.edit-icon-btn:hover { color: #409EFF; }
|
||||
|
||||
.time-cell { color: #606266; display: flex; align-items: center; gap: 5px; }
|
||||
|
||||
/* 状态标签 (带圆点) */
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.status-badge::before {
|
||||
content: '';
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.is-waiting { color: #e67e22; background: #fff3e6; }
|
||||
.is-waiting::before { background: #e67e22; }
|
||||
|
||||
.is-paused { color: #909399; background: #f4f4f5; }
|
||||
.is-paused::before { background: #909399; }
|
||||
|
||||
/* 操作按钮 */
|
||||
.action-btns .el-button {
|
||||
font-size: 18px;
|
||||
padding: 0 5px;
|
||||
color: #606266;
|
||||
}
|
||||
.action-btns .el-button:hover { color: #409EFF; }
|
||||
.action-btns .delete-btn { color: #F56C6C; }
|
||||
.action-btns .delete-btn:hover { color: #F56C6C; }
|
||||
.action-btns .delete-btn.el-button--text { color: #F56C6C !important; }
|
||||
|
||||
/* 深度选择器处理表格边距 */
|
||||
.custom-table >>> .el-table__cell { padding: 12px 0; }
|
||||
/* 邮件编辑弹窗专用样式 */
|
||||
.mail-edit-wrapper {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* 模拟富文本编辑器的工具栏感 (如果你没有引入富文本) */
|
||||
.editor-container >>> .el-textarea__inner {
|
||||
border: none;
|
||||
padding: 15px;
|
||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", sans-serif;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 让 Dialog 的 footer 更靠右 */
|
||||
.el-dialog__footer {
|
||||
padding: 20px;
|
||||
border-top: 1px solid #f0f2f5;
|
||||
}
|
||||
|
||||
/* 列表内的主题预览小字(可选) */
|
||||
.tpl-sub.mail-subject {
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #8c939d;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user