Files
EmailBill/Web/src/views/EmailRecord.vue

476 lines
12 KiB
Vue
Raw Normal View History

2025-12-25 11:20:56 +08:00
<template>
2025-12-27 21:15:26 +08:00
<div class="page-container-flex">
2025-12-25 11:20:56 +08:00
<!-- 下拉刷新区域 -->
2025-12-27 21:15:26 +08:00
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
2025-12-25 11:20:56 +08:00
<!-- 加载提示 -->
<van-loading v-if="loading && !(emailList && emailList.length)" vertical style="padding: 50px 0">
加载中...
</van-loading>
<!-- 邮件列表 -->
<van-list
v-model:loading="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<van-cell-group v-if="emailList && emailList.length" inset style="margin-top: 10px">
<van-swipe-cell
v-for="email in emailList"
:key="email.id"
>
<van-cell
:title="email.subject"
2025-12-27 16:54:08 +08:00
:label="`来自: ${email.from}\n收件: ${email.toName || email.to || '未知'}`"
2025-12-25 11:20:56 +08:00
is-link
@click="viewDetail(email)"
>
<template #value>
<div class="email-info">
<div class="email-date">{{ formatDate(email.receivedDate) }}</div>
<div class="bill-count" v-if="email.transactionCount > 0">
<span style="font-size: 12px;">已解析{{ email.transactionCount }}条账单</span>
</div>
</div>
</template>
</van-cell>
<template #right>
<van-button
square
type="danger"
text="删除"
class="delete-button"
@click="handleDelete(email)"
/>
</template>
</van-swipe-cell>
</van-cell-group>
<van-empty
v-if="!loading && !(emailList && emailList.length)"
description="暂无邮件记录"
/>
</van-list>
2025-12-28 10:23:57 +08:00
<!-- 底部安全距离 -->
<div style="height: calc(50px + env(safe-area-inset-bottom, 0px))"></div>
2025-12-25 11:20:56 +08:00
</van-pull-refresh>
<!-- 详情弹出层 -->
<van-popup
v-model:show="detailVisible"
position="bottom"
:style="{ height: '80%' }"
round
closeable
>
<div class="email-detail" v-if="currentEmail">
<div class="detail-header" style="margin-top: 10px; margin-left: 10px;">
<h3>{{ currentEmail.Subject || currentEmail.subject || '(无主题)' }}</h3>
</div>
<van-cell-group inset>
<van-cell title="发件人" :value="currentEmail.From || currentEmail.from || '未知'" />
<van-cell title="接收时间" :value="formatDate(currentEmail.ReceivedDate || currentEmail.receivedDate)" />
<van-cell title="记录时间" :value="formatDate(currentEmail.CreateTime || currentEmail.createTime)" />
<van-cell
title="已解析账单数"
:value="`${currentEmail.TransactionCount || currentEmail.transactionCount || 0}条`"
is-link
@click="viewTransactions"
v-if="(currentEmail.TransactionCount || currentEmail.transactionCount || 0) > 0"
/>
</van-cell-group>
<div class="email-content">
<h4 style="margin-left: 10px;">邮件内容</h4>
<div
v-if="currentEmail.htmlBody"
v-html="currentEmail.htmlBody"
class="content-body html-content"
></div>
<div
v-else-if="currentEmail.body"
class="content-body"
>
{{ currentEmail.body }}
</div>
<div v-else class="content-body empty-content">
暂无邮件内容
<div style="font-size: 12px; margin-top: 8px; color: #999;">
Debug: {{ Object.keys(currentEmail).join(', ') }}
</div>
</div>
</div>
<div style="margin: 16px;">
<van-button
round
block
type="primary"
:loading="refreshingAnalysis"
@click="handleRefreshAnalysis"
>
重新分析
</van-button>
</div>
</div>
</van-popup>
<!-- 账单列表弹出层 -->
<van-popup
v-model:show="transactionListVisible"
position="bottom"
:style="{ height: '70%' }"
round
closeable
>
<div class="transaction-list-popup">
<div class="list-header">
<h3 style="margin: 16px;">关联账单列表</h3>
</div>
2025-12-27 11:50:12 +08:00
<div class="bills-scroll-container">
<TransactionList
:transactions="transactionList"
:loading="false"
:finished="true"
:show-delete="false"
@click="handleTransactionClick"
/>
</div>
2025-12-25 11:20:56 +08:00
</div>
</van-popup>
<!-- 账单详情编辑弹出层 -->
<TransactionDetail
:show="transactionDetailVisible"
:transaction="currentTransaction"
@update:show="transactionDetailVisible = $event"
@save="handleTransactionSave"
/>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { showToast, showConfirmDialog } from 'vant'
import { getEmailList, getEmailDetail, deleteEmail, refreshTransactionRecords, syncEmails, getEmailTransactions } from '@/api/emailRecord'
import { getTransactionDetail } from '@/api/transactionRecord'
import TransactionList from '@/components/TransactionList.vue'
import TransactionDetail from '@/components/TransactionDetail.vue'
const emailList = ref([])
const loading = ref(false)
const refreshing = ref(false)
const finished = ref(false)
2025-12-27 22:05:50 +08:00
const pageIndex = ref(1) // 页码
const pageSize = 20 // 每页数量
2025-12-25 11:20:56 +08:00
const total = ref(0)
const detailVisible = ref(false)
const currentEmail = ref(null)
const refreshingAnalysis = ref(false)
const syncing = ref(false)
const transactionListVisible = ref(false)
const transactionList = ref([])
const transactionDetailVisible = ref(false)
const currentTransaction = ref(null)
// 加载数据
const loadData = async (isRefresh = false) => {
if (loading.value) return // 防止重复加载
if (isRefresh) {
2025-12-27 22:05:50 +08:00
pageIndex.value = 1
2025-12-25 11:20:56 +08:00
emailList.value = []
finished.value = false
}
loading.value = true
try {
2025-12-27 22:05:50 +08:00
const params = {
pageIndex: pageIndex.value,
pageSize: pageSize
2025-12-25 11:20:56 +08:00
}
const response = await getEmailList(params)
if (response.success) {
const newList = response.data || []
total.value = response.total || 0
if (isRefresh) {
emailList.value = newList
} else {
emailList.value = [...(emailList.value || []), ...newList]
}
2025-12-27 22:05:50 +08:00
// 判断是否还有更多数据返回数据少于pageSize条或为空说明没有更多了
if (newList.length === 0 || newList.length < pageSize) {
2025-12-25 11:20:56 +08:00
finished.value = true
} else {
finished.value = false
2025-12-27 22:05:50 +08:00
pageIndex.value++
2025-12-25 11:20:56 +08:00
}
} else {
showToast(response.message || '加载数据失败')
finished.value = true
}
} catch (error) {
console.error('加载数据出错:', error)
showToast('加载数据出错: ' + (error.message || '未知错误'))
finished.value = true
} finally {
loading.value = false
refreshing.value = false
}
}
// 下拉刷新
const onRefresh = () => {
loadData(true)
}
// 加载更多
const onLoad = () => {
if (!finished.value && !loading.value) {
loadData()
}
}
// 查看详情
const viewDetail = async (email) => {
try {
const response = await getEmailDetail(email.id)
console.log('详情 API 返回:', response)
if (response.success) {
currentEmail.value = response.data
console.log('currentEmail:', currentEmail.value)
detailVisible.value = true
} else {
showToast(response.message || '获取详情失败')
}
} catch (error) {
console.error('获取详情出错:', error)
showToast('获取详情失败')
}
}
// 删除
const handleDelete = async (email) => {
try {
await showConfirmDialog({
title: '提示',
message: '确定要删除这封邮件吗?',
})
const response = await deleteEmail(email.id)
if (response.success) {
showToast('删除成功')
loadData(true)
} else {
showToast(response.message || '删除失败')
}
} catch (error) {
if (error !== 'cancel') {
console.error('删除出错:', error)
showToast('删除失败')
}
}
}
// 重新分析
const handleRefreshAnalysis = async () => {
if (!currentEmail.value) return
try {
await showConfirmDialog({
title: '提示',
message: '确定要重新分析该邮件并刷新交易记录吗?',
})
refreshingAnalysis.value = true
const response = await refreshTransactionRecords(currentEmail.value.id || currentEmail.value.Id)
if (response.success) {
showToast('重新分析成功')
detailVisible.value = false
} else {
showToast(response.message || '重新分析失败')
}
} catch (error) {
if (error !== 'cancel') {
console.error('重新分析出错:', error)
showToast('重新分析失败: ' + (error.message || '未知错误'))
}
} finally {
refreshingAnalysis.value = false
}
}
// 立即同步
const handleSync = async () => {
try {
syncing.value = true
const response = await syncEmails()
if (response.success) {
showToast(response.message || '同步成功')
// 同步成功后刷新列表
loadData(true)
} else {
showToast(response.message || '同步失败')
}
} catch (error) {
console.error('同步出错:', error)
showToast('同步失败: ' + (error.message || '未知错误'))
} finally {
syncing.value = false
}
}
// 查看关联的账单列表
const viewTransactions = async () => {
if (!currentEmail.value) return
try {
const emailId = currentEmail.value.id || currentEmail.value.Id
const response = await getEmailTransactions(emailId)
if (response.success) {
transactionList.value = response.data || []
transactionListVisible.value = true
} else {
showToast(response.message || '获取账单列表失败')
}
} catch (error) {
console.error('获取账单列表出错:', error)
showToast('获取账单列表失败')
}
}
// 处理点击账单
const handleTransactionClick = async (transaction) => {
try {
const response = await getTransactionDetail(transaction.id)
if (response.success) {
currentTransaction.value = response.data
transactionDetailVisible.value = true
} else {
showToast(response.message || '获取账单详情失败')
}
} catch (error) {
console.error('获取账单详情出错:', error)
showToast('获取账单详情失败')
}
}
// 账单保存后刷新列表
const handleTransactionSave = async () => {
// 刷新账单列表
if (currentEmail.value) {
const emailId = currentEmail.value.id || currentEmail.value.Id
const response = await getEmailTransactions(emailId)
if (response.success) {
transactionList.value = response.data || []
}
}
}
// 格式化日期
const formatDate = (dateString) => {
if (!dateString) return ''
const date = new Date(dateString)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
}
onMounted(() => {
loadData(true)
})
2025-12-28 10:23:57 +08:00
// 暴露给父级方法调用
defineExpose({
handleSync
})
2025-12-25 11:20:56 +08:00
</script>
<style scoped>
2025-12-27 21:15:26 +08:00
:deep(.van-pull-refresh) {
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
2025-12-25 11:20:56 +08:00
.email-info {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 4px;
}
.bill-count {
margin-bottom: 2px;
}
.email-date {
font-size: 12px;
color: #969799;
padding-right: 10px;
}
.transaction-list-popup {
height: 100%;
2025-12-27 11:50:12 +08:00
display: flex;
flex-direction: column;
}
.list-header{
padding: 16px;
border-bottom: 1px solid #ebedf0;
2025-12-25 11:20:56 +08:00
}
.email-content {
margin-top: 16px;
}
.email-content h4 {
margin: 0 0 12px 0;
font-size: 16px;
font-weight: bold;
}
.content-body {
padding: 12px;
border-radius: 8px;
white-space: pre-wrap;
word-break: break-word;
font-size: 14px;
line-height: 1.6;
max-height: 300px;
overflow-y: auto;
margin: 0 20px;
}
@media (prefers-color-scheme: dark) {
.content-body {
background-color: #2c2c2c;
border: 1px solid #3a3a3a;
}
}
.delete-button {
height: 100%;
}
2025-12-26 18:03:52 +08:00
/* 设置页面容器背景色 */
:deep(.van-nav-bar) {
background: transparent !important;
}
2025-12-25 11:20:56 +08:00
</style>