Merge branch 'master' of https://git.nuttyreading.com/wangjinlei/tougao_web into Editorial-Board

This commit is contained in:
2026-03-18 10:35:43 +08:00
9 changed files with 1131 additions and 578 deletions

View File

@@ -1,96 +1,129 @@
<template>
<div class="admin-container">
<h2>{{ $t('mailboxMould.title') }}</h2>
<p class="subtitle">{{ $t('mailboxMould.subtitle') }}</p>
<div class="toolbar">
<el-select
v-model="filters.journalId"
:placeholder="$t('mailboxMould.journalPlaceholder')"
style="width: 300px; margin-right: 10px;"
@change="fetchList"
>
<el-option
v-for="j in journalList"
:key="j.journal_id"
:label="j.title"
:value="String(j.journal_id)"
></el-option>
</el-select>
<el-tabs v-model="activeTab" @tab-click="handleTabChange">
<!-- ========== Tab 1: Templates ========== -->
<el-tab-pane :label="$t('mailboxMould.title') || 'Templates'" name="templates">
<div class="toolbar">
<el-select
v-model="tplFilters.journalId"
:placeholder="$t('mailboxMould.journalPlaceholder')"
style="width: 300px; margin-right: 10px;"
@change="fetchTemplates"
>
<el-option
v-for="j in journalList"
:key="j.journal_id"
:label="j.title"
:value="String(j.journal_id)"
></el-option>
</el-select>
<el-select
v-model="filters.scene"
:placeholder="$t('mailboxMould.scenePlaceholder')"
clearable
style="width: 200px; margin-right: 10px;"
@change="fetchList"
@clear="fetchList"
>
<el-option :label="$t('mailboxMould.inviteSubmission')" value="invite_submission"></el-option>
<el-option :label="$t('mailboxMould.promoteCitation')" value="promote_citation"></el-option>
<el-option :label="$t('mailboxMould.generalThanks')" value="general_thanks"></el-option>
</el-select>
<el-select
v-model="tplFilters.scene"
:placeholder="$t('mailboxMould.scenePlaceholder')"
clearable
style="width: 200px; margin-right: 10px;"
@change="fetchTemplates"
@clear="fetchTemplates"
>
<el-option :label="$t('mailboxMould.inviteSubmission')" value="invite_submission"></el-option>
<el-option :label="$t('mailboxMould.promoteCitation')" value="promote_citation"></el-option>
<el-option :label="$t('mailboxMould.generalThanks')" value="general_thanks"></el-option>
</el-select>
<el-select
v-model="filters.language"
:placeholder="$t('mailboxMould.languagePlaceholder')"
clearable
style="width: 120px; margin-right: 10px;"
@change="fetchList"
@clear="fetchList"
>
<el-option label="EN" value="en"></el-option>
<el-option label="ZH" value="zh"></el-option>
</el-select>
<el-select
v-model="tplFilters.language"
:placeholder="$t('mailboxMould.languagePlaceholder')"
clearable
style="width: 120px; margin-right: 10px;"
@change="fetchTemplates"
@clear="fetchTemplates"
>
<el-option label="EN" value="en"></el-option>
<el-option label="ZH" value="zh"></el-option>
</el-select>
<el-button type="primary" icon="el-icon-search" @click="fetchList" style="margin-right: 10px;">
{{ $t('mailboxMould.searchBtn') }}
</el-button>
<el-button type="primary" icon="el-icon-search" @click="fetchTemplates" style="margin-right: 10px;">
{{ $t('mailboxMould.searchBtn') }}
</el-button>
<div class="right-actions">
<el-button type="primary" plain icon="el-icon-plus" @click="handleCreate">{{ $t('mailboxMould.createTemplate') }}</el-button>
</div>
</div>
<el-table :data="tableData" border style="width: 100%; margin-top: 20px;" v-loading="loading">
<el-table-column prop="title" :label="$t('mailboxMould.colTitle')" min-width="220">
<template slot-scope="scope">
<div class="title-cell">
<strong>{{ scope.row.title }}</strong>
<div class="right-actions">
<el-button type="primary" plain icon="el-icon-plus" @click="handleCreateTemplate">{{ $t('mailboxMould.createTemplate') }}</el-button>
</div>
</template>
</el-table-column>
<el-table-column prop="subject" :label="$t('mailboxMould.colSubject')" min-width="220">
<template slot-scope="scope">
<span>{{ scope.row.subject || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="scene" :label="$t('mailboxMould.colScene')">
<template slot-scope="scope">
<el-tag size="small" type="info">{{ scope.row.scene }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="language" :label="$t('mailboxMould.colLanguage')" width="100">
<template slot-scope="scope">
{{ String(scope.row.language || '').toUpperCase() }}
</template>
</el-table-column>
<el-table-column prop="version" :label="$t('mailboxMould.colVersion')" width="100"></el-table-column>
<el-table-column :label="$t('mailboxMould.colStatus')" width="120">
<template slot-scope="scope">
<span :class="['status-dot', scope.row.status]"></span>
{{ scope.row.status === 'active' ? $t('mailboxMould.active') : $t('mailboxMould.inactive') }}
</template>
</el-table-column>
<el-table-column width="160">
<template slot-scope="scope">
<el-button type="text" icon="el-icon-view" @click="handlePreview(scope.row)"></el-button>
<el-button type="text" icon="el-icon-edit" @click="handleEdit(scope.row)"></el-button>
<el-button type="text" icon="el-icon-delete" class="delete-btn" @click="handleDelete(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-table :data="tplTableData" border style="width: 100%; margin-top: 20px;" v-loading="tplLoading">
<el-table-column prop="title" :label="$t('mailboxMould.colTitle')" min-width="220">
<template slot-scope="scope">
<div class="title-cell"><strong>{{ scope.row.title }}</strong></div>
</template>
</el-table-column>
<el-table-column prop="subject" :label="$t('mailboxMould.colSubject')" min-width="220">
<template slot-scope="scope">
<span>{{ scope.row.subject || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="scene" :label="$t('mailboxMould.colScene')">
<template slot-scope="scope">
<el-tag size="small" type="info">{{ scope.row.scene }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="language" :label="$t('mailboxMould.colLanguage')" width="100">
<template slot-scope="scope">
{{ String(scope.row.language || '').toUpperCase() }}
</template>
</el-table-column>
<el-table-column prop="version" :label="$t('mailboxMould.colVersion')" width="100"></el-table-column>
<el-table-column :label="$t('mailboxMould.colStatus')" width="120">
<template slot-scope="scope">
<span :class="['status-dot', scope.row.status]"></span>
{{ scope.row.status === 'active' ? $t('mailboxMould.active') : $t('mailboxMould.inactive') }}
</template>
</el-table-column>
<el-table-column width="160">
<template slot-scope="scope">
<el-button type="text" icon="el-icon-view" @click="handlePreviewTemplate(scope.row)"></el-button>
<el-button type="text" icon="el-icon-edit" @click="handleEditTemplate(scope.row)"></el-button>
<el-button type="text" icon="el-icon-delete" class="delete-btn" @click="handleDeleteTemplate(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- ========== Tab 2: Styles ========== -->
<el-tab-pane :label="$t('mailboxStyle.title') || 'Styles'" name="styles">
<div class="toolbar">
<div class="right-actions">
<el-button type="primary" icon="el-icon-refresh" @click="fetchStyles">{{ $t('mailboxStyle.searchBtn') }}</el-button>
<el-button type="primary" plain icon="el-icon-plus" @click="handleCreateStyle">{{ $t('mailboxStyle.createStyle') }}</el-button>
</div>
</div>
<el-table :data="styleTableData" border style="width: 100%; margin-top: 20px;" v-loading="styleLoading">
<el-table-column prop="name" :label="$t('mailboxStyle.colName')" min-width="220">
<template slot-scope="scope">
<div class="title-cell"><strong>{{ scope.row.title }}</strong></div>
</template>
</el-table-column>
<el-table-column prop="description" :label="$t('mailboxStyle.colDescription')" min-width="220">
<template slot-scope="scope">
<span>{{ scope.row.description || '-' }}</span>
</template>
</el-table-column>
<el-table-column width="160">
<template slot-scope="scope">
<el-button type="text" icon="el-icon-view" @click="handlePreviewStyle(scope.row)"></el-button>
<el-button type="text" icon="el-icon-edit" @click="handleEditStyle(scope.row)"></el-button>
<el-button type="text" icon="el-icon-delete" class="delete-btn" @click="handleDeleteStyle(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<!-- 预览弹窗两个 Tab 共用 -->
<el-dialog
:title="$t('mailboxMould.previewTitle')"
:visible.sync="previewVisible"
@@ -107,22 +140,32 @@
<script>
const API = {
listTemplates: 'api/mail_template/listTemplates',
listStyles: 'api/mail_template/listStyles',
getAllJournal: 'api/Journal/getAllJournal',
deleteTemplate: 'api/mail_template/deleteTemplate'
deleteTemplate: 'api/mail_template/deleteTemplate',
deleteStyle: 'api/mail_template/deleteStyle'
};
export default {
data() {
return {
searchQuery: '',
loading: false,
activeTab: 'templates',
journalList: [],
filters: {
// --- Templates ---
tplLoading: false,
tplFilters: {
journalId: '',
scene: '',
language: ''
},
tableData: [],
tplTableData: [],
// --- Styles ---
styleLoading: false,
styleTableData: [],
// --- 共用预览 ---
previewVisible: false,
previewContent: ''
};
@@ -131,6 +174,7 @@ export default {
this.loadJournals();
},
methods: {
// ========== 公共 ==========
loadJournals() {
this.$api
.post(API.getAllJournal, {})
@@ -142,27 +186,36 @@ export default {
}));
this.journalList = mapped;
if (mapped.length > 0) {
this.filters.journalId = String(mapped[0].journal_id);
this.tplFilters.journalId = String(mapped[0].journal_id);
}
this.fetchList();
this.fetchTemplates();
})
.catch(() => {
this.journalList = [];
});
},
fetchList() {
this.loading = true;
handleTabChange(tab) {
if (tab.name === 'templates' && this.tplTableData.length === 0) {
this.fetchTemplates();
} else if (tab.name === 'styles' && this.styleTableData.length === 0) {
this.fetchStyles();
}
},
// ========== Templates ==========
fetchTemplates() {
this.tplLoading = true;
const params = {
journal_id: this.filters.journalId || '',
scene: this.filters.scene || '',
language: this.filters.language || ''
journal_id: this.tplFilters.journalId || '',
scene: this.tplFilters.scene || '',
language: this.tplFilters.language || ''
};
this.$api
.post(API.listTemplates, params)
.then(res => {
this.loading = false;
this.tplLoading = false;
const list = (res && res.data && res.data.list) || [];
this.tableData = (Array.isArray(list) ? list : []).map(item => ({
this.tplTableData = (Array.isArray(list) ? list : []).map(item => ({
id: item.template_id || item.id,
template_id: item.template_id || item.id,
title: item.title,
@@ -176,24 +229,22 @@ export default {
}));
})
.catch(() => {
this.loading = false;
this.tableData = [];
this.tplLoading = false;
this.tplTableData = [];
});
},
handleCreate() {
handleCreateTemplate() {
this.$router.push({ path: '/mailboxMouldDetail' });
},
handleEdit(row) {
console.log('Editing:', row);
// 跳转到详情页,后续可带上模板 ID
handleEditTemplate(row) {
const templateId = row && (row.template_id || row.id);
this.$router.push({ path: '/mailboxMouldDetail', query: templateId ? { template_id: String(templateId) } : {} });
},
handlePreview(row) {
this.previewContent = row && row.body_html ? row.body_html : (row && row.body ? row.body : '');
handlePreviewTemplate(row) {
this.previewContent = row && row.body_html ? row.body_html : '';
this.previewVisible = true;
},
handleDelete(row) {
handleDeleteTemplate(row) {
const templateId = row && (row.template_id || row.id);
if (!templateId) return;
this.$confirm(this.$t('mailboxMould.deleteConfirm'), this.$t('mailboxMould.colActions'), {
@@ -204,7 +255,63 @@ export default {
this.$api.post(API.deleteTemplate, { template_id: String(templateId) }).then(res => {
if (res && res.code === 0) {
this.$message.success(this.$t('mailboxMould.deleteSuccess'));
this.fetchList();
this.fetchTemplates();
} else {
this.$message.error((res && res.msg) || this.$t('mailboxMould.deleteFail'));
}
}).catch(() => {
this.$message.error(this.$t('mailboxMould.deleteFail'));
});
}).catch(() => {});
},
// ========== Styles ==========
fetchStyles() {
this.styleLoading = true;
this.$api
.post(API.listStyles, {})
.then(res => {
this.styleLoading = false;
const list = (res && res.data && res.data.list) || [];
this.styleTableData = (Array.isArray(list) ? list : []).map(item => ({
id: item.style_id || item.id,
style_id: item.style_id || item.id,
title: item.name,
header_html: item.header_html,
footer_html: item.footer_html,
description: item.description || ''
}));
})
.catch(() => {
this.styleLoading = false;
this.styleTableData = [];
});
},
handleCreateStyle() {
this.$router.push({ path: '/mailboxStyleDetail' });
},
handleEditStyle(row) {
const styleId = row && (row.style_id || row.id);
this.$router.push({ path: '/mailboxStyleDetail', query: styleId ? { style_id: String(styleId) } : {} });
},
handlePreviewStyle(row) {
const header = (row && row.header_html) || '';
const footer = (row && row.footer_html) || '';
this.previewContent = `${header}${footer}`;
this.previewVisible = true;
},
handleDeleteStyle(row) {
const styleId = row && (row.style_id || row.id);
if (!styleId) return;
this.$confirm(this.$t('mailboxMould.deleteConfirm'), this.$t('mailboxMould.colActions'), {
confirmButtonText: this.$t('mailboxMould.confirm'),
cancelButtonText: this.$t('mailboxMould.cancel'),
type: 'warning'
}).then(() => {
this.$api.post(API.deleteStyle, { style_id: String(styleId) }).then(res => {
if (res && res.code === 0) {
this.$message.success(this.$t('mailboxMould.deleteSuccess'));
this.fetchStyles();
} else {
this.$message.error((res && res.msg) || this.$t('mailboxMould.deleteFail'));
}
@@ -257,4 +364,4 @@ export default {
border: 1px solid #eee;
background: #fff;
}
</style>
</style>