|
|
|
|
@ -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 => {
|
|
|
|
|
// 根据API返回的数据结构,data字段包含记录数组
|
|
|
|
|
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>
|