|
<script setup lang="ts"> |
|
import { computed, ref, watch, onMounted } from 'vue'; |
|
import { MessagePlugin, DialogPlugin } from 'tdesign-vue-next'; |
|
import { accountApi, type Account } from '../services/accountApi'; |
|
import { mailApi } from '../services/mailApi'; |
|
const loading = ref(false); |
|
const pagination = ref({ |
|
current: 1, |
|
total: 0, |
|
pageSize: 18 |
|
}); |
|
|
|
const allData = ref<Account[]>([]); |
|
const userSearch = ref(''); |
|
const sort = ref<{ sortBy: string; descending: boolean }>({ |
|
sortBy: '', |
|
descending: true |
|
}); |
|
|
|
|
|
|
|
const columns = [ |
|
{ colKey: 'email', title: '用户', width: 200, sorter: true }, |
|
{ |
|
colKey: 'action', |
|
title: '操作', |
|
width: 400, |
|
} |
|
]; |
|
|
|
|
|
|
|
const actionOptions = (row: Account) => [ |
|
{ content: '刷新认证', value: 'refresh', onClick: () => handleRefreshAuth(row) }, |
|
{ content: '最新邮件', value: 'latest', onClick: () => handleLatestMails(row) }, |
|
{ content: '所有邮件', value: 'all', onClick: () => handleAllMails(row) }, |
|
{ content: '发送邮件', value: 'send', onClick: () => handleSendMail(row) }, |
|
]; |
|
|
|
|
|
const handleRefreshAuth = async (row: Account) => { |
|
try { |
|
loading.value = true; |
|
await mailApi.refreshAuth(row.email); |
|
MessagePlugin.success('认证已刷新'); |
|
|
|
} catch (error) { |
|
MessagePlugin.error('刷新认证失败'); |
|
} finally { |
|
loading.value = false; |
|
} |
|
}; |
|
|
|
interface ParsedEmail { |
|
subject: string; |
|
from: string; |
|
to: string[]; |
|
date: string; |
|
text: string; |
|
html: string; |
|
} |
|
|
|
const showEmailDialog = ref(false); |
|
const currentEmail = ref<any>(null); |
|
|
|
const handleLatestMails = async (row: Account) => { |
|
try { |
|
loading.value = true; |
|
const result = await mailApi.getLatestMails(row.email); |
|
if (result) { |
|
currentEmail.value = result; |
|
showEmailDialog.value = true; |
|
} else { |
|
MessagePlugin.info('没有找到邮件'); |
|
} |
|
} catch (error:any) { |
|
MessagePlugin.error(`${error.message}`); |
|
} finally { |
|
loading.value = false; |
|
} |
|
}; |
|
|
|
|
|
const showAllMailsDialog = ref(false); |
|
const allMailsList = ref<any[]>([]); |
|
const mailsLoading = ref(false); |
|
|
|
const handleAllMails = async (row: Account) => { |
|
try { |
|
mailsLoading.value = true; |
|
showAllMailsDialog.value = true; |
|
allMailsList.value= [] |
|
const emails = await mailApi.getAllMails(row.email); |
|
allMailsList.value = emails; |
|
} catch (error: any) { |
|
MessagePlugin.error(`获取邮件失败: ${error.message}`); |
|
} finally { |
|
mailsLoading.value = false; |
|
} |
|
}; |
|
|
|
|
|
const viewMailDetail = (email: any) => { |
|
currentEmail.value = email; |
|
showEmailDialog.value = true; |
|
}; |
|
|
|
|
|
|
|
|
|
const showSendDialog = ref(false); |
|
const sendMailForm = ref({ |
|
to: '', |
|
subject: '', |
|
body: '', |
|
isHtml: false |
|
}); |
|
const currentMailAccount = ref<Account | null>(null); |
|
|
|
|
|
const handleSendMail = async (row: Account) => { |
|
currentMailAccount.value = row; |
|
sendMailForm.value = { |
|
to: '', |
|
subject: '', |
|
body: '', |
|
isHtml: false |
|
}; |
|
showSendDialog.value = true; |
|
}; |
|
|
|
|
|
const submitSendMail = async () => { |
|
if (!currentMailAccount.value) return; |
|
|
|
try { |
|
loading.value = true; |
|
await mailApi.sendMail({ |
|
email: currentMailAccount.value.email, |
|
to: sendMailForm.value.to.split(',').map(e => e.trim()), |
|
subject: sendMailForm.value.subject, |
|
body: sendMailForm.value.body, |
|
isHtml: sendMailForm.value.isHtml |
|
}); |
|
MessagePlugin.success('邮件发送成功'); |
|
showSendDialog.value = false; |
|
} catch (error: any) { |
|
MessagePlugin.error(`发送失败: ${error.message}`); |
|
} finally { |
|
loading.value = false; |
|
} |
|
}; |
|
|
|
|
|
const handleSort = (sortInfo: { sortBy: string; descending: boolean }) => { |
|
sort.value = sortInfo; |
|
pagination.value.current = 1; |
|
}; |
|
|
|
|
|
|
|
const processedData = computed(() => { |
|
let result = [...allData.value]; |
|
|
|
|
|
if (userSearch.value) { |
|
result = result.filter(log => |
|
log.email.toLowerCase().includes(userSearch.value.toLowerCase()) |
|
); |
|
} |
|
|
|
pagination.value.total = result.length; |
|
|
|
|
|
const start = (pagination.value.current - 1) * pagination.value.pageSize; |
|
const end = start + pagination.value.pageSize; |
|
return result.slice(start, end); |
|
}); |
|
|
|
|
|
const fetchData = async () => { |
|
loading.value = true; |
|
try { |
|
const data = await accountApi.get(); |
|
allData.value = data; |
|
pagination.value.total = data.length; |
|
} catch (error) { |
|
MessagePlugin.error('获取数据失败'); |
|
} finally { |
|
loading.value = false; |
|
} |
|
}; |
|
|
|
|
|
onMounted(() => { |
|
fetchData(); |
|
}); |
|
|
|
|
|
</script> |
|
|
|
<template> |
|
<div class="w-full h-full flex flex-col p-2 md:p-5 gap-2 md:gap-5"> |
|
<div class="flex flex-col md:flex-row md:items-center md:justify-between"> |
|
<div class="flex flex-col md:flex-row md:items-center gap-4"> |
|
<t-input v-model="userSearch" class="w-full md:w-40" placeholder="搜索用户" clearable /> |
|
</div> |
|
</div> |
|
|
|
<div class="overflow-x-auto flex-1 overflow-y-auto"> |
|
<t-table :data="processedData" :loading="loading" :columns="columns" row-key="datetime" hover |
|
:sort="sort" @sort-change="handleSort" size="small" class="min-w-full"> |
|
<template #action="{ row }"> |
|
|
|
<div class="hidden md:flex flex-wrap gap-2"> |
|
<t-button size="small" variant="outline" @click="handleRefreshAuth(row)">刷新认证</t-button> |
|
<t-button size="small" variant="outline" @click="handleLatestMails(row)">最新邮件</t-button> |
|
<t-button size="small" variant="outline" @click="handleAllMails(row)">所有邮件</t-button> |
|
<t-button size="small" variant="outline" @click="handleSendMail(row)">发送邮件</t-button> |
|
</div> |
|
|
|
|
|
<div class="md:hidden"> |
|
<t-dropdown :options="actionOptions(row)"> |
|
<t-button variant="outline" size="small">操作 <t-icon name="chevron-down" /></t-button> |
|
</t-dropdown> |
|
</div> |
|
</template> |
|
</t-table> |
|
</div> |
|
|
|
<div class="flex justify-end"> |
|
<t-pagination v-model="pagination.current" :total="pagination.total" :page-size="pagination.pageSize" |
|
size="small" /> |
|
</div> |
|
|
|
|
|
<t-dialog |
|
:visible="showEmailDialog" |
|
:header="currentEmail?.subject" |
|
@close="showEmailDialog = false" |
|
:width="800" |
|
:footer="false" |
|
class="email-detail-dialog" |
|
> |
|
<template v-if="currentEmail"> |
|
<div class="email-details"> |
|
<div class="email-meta"> |
|
<p><strong>发件人:</strong> {{ currentEmail.from }}</p> |
|
<p><strong>收件人:</strong> {{ currentEmail.to?.join(', ') }}</p> |
|
<p><strong>时间:</strong> {{ new Date(currentEmail.date.received).toLocaleString() }}</p> |
|
<p v-if="currentEmail.metadata?.location"><strong>位置:</strong> {{ currentEmail.metadata.location }}</p> |
|
<p v-if="currentEmail.metadata?.ip"><strong>IP:</strong> {{ currentEmail.metadata.ip }}</p> |
|
<p v-if="currentEmail.metadata?.platform"><strong>平台:</strong> {{ currentEmail.metadata.platform }}</p> |
|
<p v-if="currentEmail.metadata?.browser"><strong>浏览器:</strong> {{ currentEmail.metadata.browser }}</p> |
|
<p><strong>重要性:</strong> {{ currentEmail.importance }}</p> |
|
<p><strong>状态:</strong> {{ currentEmail.isRead ? '已读' : '未读' }}</p> |
|
</div> |
|
<div class="email-content" v-html="currentEmail.html || currentEmail.text"></div> |
|
<div v-if="currentEmail.hasAttachments" class="email-attachments">包含附件</div> |
|
<div class="email-actions"> |
|
<a :href="currentEmail.webLink" target="_blank">在Outlook中查看</a> |
|
</div> |
|
</div> |
|
</template> |
|
</t-dialog> |
|
|
|
|
|
<t-dialog |
|
:visible="showAllMailsDialog" |
|
header="所有邮件" |
|
@close="showAllMailsDialog = false" |
|
:width="900" |
|
:footer="false" |
|
> |
|
<t-loading :loading="mailsLoading"> |
|
<div class="emails-list"> |
|
<t-list> |
|
<t-list-item |
|
v-for="email in allMailsList" |
|
:key="email.id" |
|
@click="viewMailDetail(email)" |
|
class="email-list-item" |
|
> |
|
<div class="flex flex-col gap-1 w-full cursor-pointer hover:bg-gray-50 p-2"> |
|
<div class="flex justify-between"> |
|
<span class="font-medium">{{ email.subject || '(无主题)' }}</span> |
|
<span class="text-gray-500 text-sm"> |
|
{{ new Date(email.date.received).toLocaleString() }} |
|
</span> |
|
</div> |
|
<div class="flex justify-between text-sm"> |
|
<span class="text-gray-600">发件人: {{ email.from }}</span> |
|
<span :class="{'text-green-600': email.isRead, 'text-red-600': !email.isRead}"> |
|
{{ email.isRead ? '已读' : '未读' }} |
|
</span> |
|
</div> |
|
</div> |
|
</t-list-item> |
|
</t-list> |
|
</div> |
|
</t-loading> |
|
</t-dialog> |
|
|
|
|
|
<t-dialog |
|
:visible="showSendDialog" |
|
header="发送邮件" |
|
@close="showSendDialog = false" |
|
:width="600" |
|
:footer="false" |
|
> |
|
<template v-if="currentMailAccount"> |
|
<div class="send-mail-form"> |
|
<t-form> |
|
<t-form-item label="发件人"> |
|
<t-input disabled :value="currentMailAccount.email" /> |
|
</t-form-item> |
|
<t-form-item label="收件人"> |
|
<t-input v-model="sendMailForm.to" placeholder="多个收件人请用逗号分隔" /> |
|
</t-form-item> |
|
<t-form-item label="主题"> |
|
<t-input v-model="sendMailForm.subject" /> |
|
</t-form-item> |
|
<t-form-item label="内容"> |
|
<t-textarea v-model="sendMailForm.body" :rows="6" /> |
|
</t-form-item> |
|
<t-form-item> |
|
<t-checkbox v-model="sendMailForm.isHtml">HTML 格式</t-checkbox> |
|
</t-form-item> |
|
<t-form-item> |
|
<div class="flex justify-end gap-2"> |
|
<t-button theme="default" @click="showSendDialog = false">取消</t-button> |
|
<t-button theme="primary" @click="submitSendMail" :loading="loading">发送</t-button> |
|
</div> |
|
</t-form-item> |
|
</t-form> |
|
</div> |
|
</template> |
|
</t-dialog> |
|
</div> |
|
</template> |
|
|
|
<style scoped> |
|
@media (max-width: 768px) { |
|
:deep(.t-table__header) { |
|
font-size: 14px; |
|
} |
|
|
|
:deep(.t-table td) { |
|
font-size: 13px; |
|
} |
|
} |
|
|
|
.email-details { |
|
max-height: 60vh; |
|
overflow-y: auto; |
|
padding: 16px; |
|
} |
|
|
|
.email-content { |
|
margin-top: 16px; |
|
border-top: 1px solid #eee; |
|
padding-top: 16px; |
|
} |
|
|
|
.email-meta { |
|
background: #f5f5f5; |
|
padding: 12px; |
|
border-radius: 4px; |
|
margin-bottom: 16px; |
|
} |
|
|
|
.email-actions { |
|
margin-top: 16px; |
|
text-align: right; |
|
} |
|
|
|
.email-attachments { |
|
margin-top: 16px; |
|
color: #666; |
|
font-style: italic; |
|
} |
|
|
|
.send-mail-form { |
|
padding: 16px; |
|
} |
|
|
|
.emails-list { |
|
max-height: 60vh; |
|
overflow-y: auto; |
|
} |
|
|
|
.email-list-item { |
|
border-bottom: 1px solid #eee; |
|
} |
|
|
|
.email-list-item:last-child { |
|
border-bottom: none; |
|
} |
|
|
|
|
|
:deep(.email-detail-dialog) { |
|
z-index: 3000 !important; |
|
} |
|
|
|
|
|
:deep(.t-dialog) { |
|
z-index: 2000; |
|
} |
|
</style> |
|
|