修改发送记录

dev
lynn 5 months ago
parent b55cfd9bab
commit 22f45ff184

@ -340,30 +340,43 @@
<div class="tab-header">
<h4>发送记录</h4>
<el-select v-model="historyFilter" placeholder="全部状态" clearable>
<el-option label="全部状态" value=""></el-option>
<el-option label="发送成功" value="sent"></el-option>
<el-option label="发送失败" value="failed"></el-option>
<el-option label="等待发送" value="pending"></el-option>
</el-select>
<div class="header-actions">
<el-select v-model="historyFilter" placeholder="全部状态" clearable>
<el-option label="全部状态" value=""></el-option>
<el-option label="已发送" value="1"></el-option>
<el-option label="待发送" value="0"></el-option>
</el-select>
<el-button type="primary" @click="refreshSendHistory" :loading="refreshing">
<i class="el-icon-refresh"></i>
刷新记录
</el-button>
</div>
</div>
<el-table :data="filteredHistory" @row-click="toggleHistoryDetail">
<el-table-column prop="sendTime" label="发送时间"></el-table-column>
<el-table-column prop="templateName" label="模板名称"></el-table-column>
<el-table-column prop="recipientCount" label="收件人数量">
<template #default="scope">{{ scope.row.recipientCount }}</template>
<el-table :data="paginatedHistory" @row-click="toggleHistoryDetail">
<el-table-column prop="send_time" label="发送时间">
<template #default="scope">
{{ scope.row.send_time || scope.row.created_at || '-' }}
</template>
</el-table-column>
<el-table-column label="模板名称">
<template #default="scope">
{{ scope.row.email_template ? scope.row.email_template.title : '-' }}
</template>
</el-table-column>
<el-table-column label="收件人数">
<template #default="scope">{{ scope.row.email_record_users_count }}</template>
</el-table-column>
<el-table-column label="成功/失败">
<template #default="scope">
<span style="color: #67C23A;">{{ scope.row.successCount }}</span> /
<span style="color: #F56C6C;">{{ scope.row.failCount }}</span>
<span style="color: #67C23A;">{{ scope.row.success_count }}</span> /
<span style="color: #F56C6C;">{{ scope.row.fail_count }}</span>
</template>
</el-table-column>
<el-table-column prop="status" label="状态">
<el-table-column label="状态">
<template #default="scope">
<el-tag :type="scope.row.status === 'completed' ? 'success' : 'warning'">
{{ scope.row.status === 'completed' ? '已完成' : '进行中' }}
<el-tag :type="scope.row.status === 1 ? 'success' : 'warning'">
{{ scope.row.status === 1 ? '已发送' : '待发送' }}
</el-tag>
</template>
</el-table-column>
@ -373,6 +386,19 @@
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<div class="pagination-container">
<el-pagination
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="filteredHistory.length"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange">
</el-pagination>
</div>
</el-tab-pane>
</el-tabs>
</div>
@ -426,6 +452,172 @@
</span>
</template>
</el-dialog>
<!-- 发送记录详情模态框 -->
<el-dialog
title="发送记录详情"
:visible.sync="showDetailModal"
width="60%"
:before-close="() => showDetailModal = false"
class="detail-modal"
center>
<div v-if="detailRecord" class="detail-content">
<!-- 基本信息卡片 -->
<div class="detail-section">
<h4 class="section-title">
<i class="el-icon-info"></i>
基本信息
</h4>
<el-row :gutter="20">
<el-col :span="12">
<div class="info-item">
<label>邮件主题</label>
<span class="info-value">{{ detailRecord.subject }}</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<label>模板名称</label>
<span class="info-value">{{ detailRecord.templateName }}</span>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<label>发送状态</label>
<el-tag :type="getStatusType(detailRecord.status)" size="medium">
{{ detailRecord.statusText }}
</el-tag>
</div>
</el-col>
<el-col :span="12">
<div class="info-item">
<label>创建时间</label>
<span class="info-value">{{ detailRecord.createdTime }}</span>
</div>
</el-col>
<el-col :span="12" v-if="detailRecord.sendTime">
<div class="info-item">
<label>发送时间</label>
<span class="info-value">{{ detailRecord.sendTime }}</span>
</div>
</el-col>
</el-row>
</div>
<!-- 统计信息卡片 -->
<div class="detail-section">
<h4 class="section-title">
<i class="el-icon-data-line"></i>
发送统计
</h4>
<el-row :gutter="20">
<el-col :span="8">
<div class="stat-card">
<div class="stat-icon total">
<i class="el-icon-user"></i>
</div>
<div class="stat-content">
<div class="stat-number">{{ detailRecord.totalRecipients }}</div>
<div class="stat-label">总收件人</div>
</div>
</div>
</el-col>
<el-col :span="8">
<div class="stat-card">
<div class="stat-icon success">
<i class="el-icon-check"></i>
</div>
<div class="stat-content">
<div class="stat-number">{{ detailRecord.successCount }}</div>
<div class="stat-label">发送成功</div>
</div>
</div>
</el-col>
<el-col :span="8" v-if="detailRecord.failCount > 0">
<div class="stat-card">
<div class="stat-icon fail">
<i class="el-icon-close"></i>
</div>
<div class="stat-content">
<div class="stat-number">{{ detailRecord.failCount }}</div>
<div class="stat-label">发送失败</div>
</div>
</div>
</el-col>
</el-row>
</div>
<!-- 邮件内容预览 -->
<div class="detail-section" v-if="detailRecord.templateContent">
<h4 class="section-title">
<i class="el-icon-document"></i>
邮件内容预览
</h4>
<div class="email-content-preview">
<div class="preview-header">
<strong>主题</strong>{{ detailRecord.subject }}
</div>
<div class="preview-body" v-html="detailRecord.templateContent"></div>
</div>
</div>
<!-- 收件人详情 -->
<div class="detail-section">
<h4 class="section-title">
<i class="el-icon-user-solid"></i>
收件人详情 ({{ detailRecord.recipients.length }})
</h4>
<el-table :data="detailRecord.recipients" size="small" :max-height="300">
<el-table-column prop="email" label="邮箱地址" min-width="200"></el-table-column>
<el-table-column label="发送状态" width="120">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)" size="small">
{{ getStatusText(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="send_time" label="发送时间" width="180"></el-table-column>
<el-table-column prop="reason" label="失败原因" min-width="150">
<template #default="scope">
<span v-if="scope.row.reason" class="fail-reason">{{ scope.row.reason }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="变量数据" min-width="200">
<template #default="scope">
<el-button size="mini" type="text" @click="showVarData(scope.row.var_data)">
查看数据
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDetailModal = false">关闭</el-button>
</span>
</template>
</el-dialog>
<!-- 变量数据查看弹窗 -->
<el-dialog
title="收件人变量数据"
:visible.sync="showVarDataModal"
width="50%"
:before-close="() => showVarDataModal = false">
<div v-if="currentVarData" class="var-data-content">
<el-descriptions :column="2" border>
<el-descriptions-item
v-for="(value, key) in currentVarData"
:key="key"
:label="key">
{{ value }}
</el-descriptions-item>
</el-descriptions>
</div>
</el-dialog>
</div>
</template>
@ -441,11 +633,18 @@ export default {
return {
activeTab: 'templates',
showTemplateModal: false,
showDetailModal: false,
showSendProgress: false,
sendProgress: 0,
selectAll: false,
selectedTemplateId: null,
historyFilter: '',
detailRecord: null,
showVarDataModal: false,
currentVarData: null,
refreshing: false,
currentPage: 1,
pageSize: 20,
//
templateForm: {
@ -490,6 +689,10 @@ export default {
if (val === 'history') {
this.loadSendHistory()
}
},
historyFilter() {
//
this.currentPage = 1
}
},
computed: {
@ -534,7 +737,13 @@ export default {
filteredHistory() {
if (!this.historyFilter) return this.sendHistory
return this.sendHistory.filter(record => record.status === this.historyFilter)
return this.sendHistory.filter(record => record.status.toString() === this.historyFilter)
},
paginatedHistory() {
const start = (this.currentPage - 1) * this.pageSize
const end = start + this.pageSize
return this.filteredHistory.slice(start, end)
}
},
@ -881,7 +1090,19 @@ export default {
.then(res => {
this.$message.success('邮件发送请求已提交!')
this.showSendProgress = false
//
//
this.activeTab = 'history'
//
this.loadSendHistory()
//
this.sendForm = {
subject: '',
sendMode: 'now',
scheduleTime: null,
testSend: false
}
//
this.selectedRecipients = []
})
.catch(err => {
this.$message.error('邮件发送失败')
@ -891,8 +1112,21 @@ export default {
//
toggleHistoryDetail(record) {
//
console.log('查看记录详情:', record)
//
this.detailRecord = {
subject: record.subject,
templateName: record.email_template ? record.email_template.title : '未知模板',
templateContent: record.email_template ? record.email_template.content : '',
totalRecipients: record.email_record_users_count,
successCount: record.success_count,
failCount: record.fail_count,
status: record.status,
statusText: this.getStatusText(record.status),
sendTime: record.send_time || record.created_at,
createdTime: record.created_at,
recipients: record.email_record_users || []
}
this.showDetailModal = true
},
//
@ -947,31 +1181,63 @@ export default {
getStatusType(status) {
const statusMap = {
pending: 'warning',
sending: 'primary',
sent: 'success',
failed: 'danger'
0: 'warning', //
1: 'success', //
2: 'danger' //
}
return statusMap[status] || 'info'
},
getStatusText(status) {
const statusMap = {
pending: '等待发送',
sending: '发送中',
sent: '发送成功',
failed: '发送失败'
0: '待发送',
1: '已发送',
2: '发送失败'
}
return statusMap[status] || '未知状态'
},
loadSendHistory() {
getEmailRecordList().then(res => {
// APIdata
this.sendHistory = res.data || []
}).catch(error => {
console.error('获取发送记录失败:', error)
this.$message.error('获取发送记录失败')
})
}
},
//
refreshSendHistory() {
this.refreshing = true
getEmailRecordList().then(res => {
this.sendHistory = res.data || []
this.$message.success('发送记录已刷新')
}).catch(error => {
console.error('刷新发送记录失败:', error)
this.$message.error('刷新发送记录失败')
}).finally(() => {
this.refreshing = false
})
},
//
handleSizeChange(newSize) {
this.pageSize = newSize
this.currentPage = 1 //
},
handleCurrentChange(newPage) {
this.currentPage = newPage
},
//
showVarData(varData) {
this.currentVarData = varData
this.showVarDataModal = true
},
}
}
</script>
@ -1014,6 +1280,22 @@ export default {
margin-bottom: 20px;
}
.header-actions {
display: flex;
align-items: center;
gap: 12px;
}
.pagination-container {
display: flex;
justify-content: center;
margin-top: 20px;
padding: 20px 0;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.tab-footer, .tab-navigation {
display: flex;
justify-content: flex-end;
@ -1367,6 +1649,21 @@ export default {
text-align: center;
}
.header-actions {
flex-direction: column;
gap: 10px;
width: 100%;
}
.header-actions .el-select {
width: 100%;
}
.pagination-container {
margin-top: 15px;
padding: 15px 0;
}
.tab-navigation {
flex-direction: column;
gap: 10px;
@ -2601,4 +2898,200 @@ export default {
margin-bottom: 0;
font-size: 14px;
}
/* 发送记录详情模态框样式 */
:deep(.detail-modal) {
.el-dialog__body {
padding: 0;
}
}
.detail-content {
padding: 20px;
max-height: 60vh;
overflow-y: auto;
}
.detail-section {
background: white;
border-radius: 12px;
padding: 24px;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border: 1px solid #e9ecef;
}
.section-title {
font-size: 18px;
font-weight: 600;
color: #2c3e50;
margin: 0 0 20px 0;
display: flex;
align-items: center;
gap: 8px;
padding-bottom: 12px;
border-bottom: 2px solid #f0f0f0;
}
.section-title i {
color: #409EFF;
font-size: 20px;
}
.info-item {
margin-bottom: 16px;
display: flex;
align-items: center;
}
.info-item label {
font-weight: 600;
color: #606266;
min-width: 100px;
margin-right: 12px;
}
.info-value {
color: #2c3e50;
font-weight: 500;
}
/* 统计卡片样式 */
.stat-card {
display: flex;
align-items: center;
padding: 20px;
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border-radius: 12px;
border: 1px solid #e9ecef;
transition: all 0.3s ease;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
}
.stat-icon {
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16px;
font-size: 24px;
color: white;
}
.stat-icon.total {
background: linear-gradient(135deg, #409EFF 0%, #36a3f7 100%);
}
.stat-icon.success {
background: linear-gradient(135deg, #67C23A 0%, #85ce61 100%);
}
.stat-icon.fail {
background: linear-gradient(135deg, #F56C6C 0%, #f78989 100%);
}
.stat-content {
flex: 1;
}
.stat-number {
font-size: 28px;
font-weight: 700;
color: #2c3e50;
line-height: 1;
margin-bottom: 4px;
}
.stat-label {
font-size: 14px;
color: #606266;
font-weight: 500;
}
/* 邮件内容预览样式 */
.email-content-preview {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 8px;
overflow: hidden;
}
.preview-header {
background: #e9ecef;
padding: 12px 16px;
border-bottom: 1px solid #dee2e6;
font-weight: 500;
color: #495057;
}
.preview-body {
padding: 16px;
max-height: 200px;
overflow-y: auto;
line-height: 1.6;
color: #2c3e50;
}
.preview-body :deep(p) {
margin: 8px 0;
}
.preview-body :deep(strong) {
font-weight: 600;
}
/* 收件人表格样式 */
.fail-reason {
color: #F56C6C;
font-size: 12px;
background: #fef0f0;
padding: 2px 6px;
border-radius: 4px;
}
/* 变量数据弹窗样式 */
.var-data-content {
padding: 20px 0;
}
:deep(.el-descriptions__label) {
font-weight: 600;
color: #606266;
}
:deep(.el-descriptions__content) {
color: #2c3e50;
}
/* 响应式设计 */
@media (max-width: 768px) {
.detail-content {
padding: 16px;
}
.detail-section {
padding: 16px;
}
.stat-card {
padding: 16px;
margin-bottom: 16px;
}
.stat-icon {
width: 50px;
height: 50px;
font-size: 20px;
}
.stat-number {
font-size: 24px;
}
}
</style>
Loading…
Cancel
Save