Files
EmailBill/Web/src/views/EmailRecord.vue
孙诚 239d9dcae3
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 14s
Docker Build & Deploy / Deploy to Production (push) Successful in 6s
debugger
2025-12-25 17:20:50 +08:00

474 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="email-record-container" style="padding-bottom: calc(50px + env(safe-area-inset-bottom, 0px));">
<!-- 顶部导航栏 -->
<van-nav-bar title="邮件记录" fixed placeholder>
<template #right>
<van-button
size="small"
type="primary"
:loading="syncing"
@click="handleSync"
>
立即同步
</van-button>
</template>
</van-nav-bar>
<!-- 下拉刷新区域 -->
<van-pull-refresh v-model="refreshing" @refresh="onRefresh" class="refresh-wrapper">
<!-- 加载提示 -->
<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"
:label="`来自: ${email.from}`"
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>
</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>
<TransactionList
:transactions="transactionList"
:loading="false"
:finished="true"
:show-delete="false"
@click="handleTransactionClick"
/>
</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)
const lastId = ref(null) // 游标分页记录最后一条记录的ID
const lastTime = ref(null) // 游标分页:记录最后一条记录的时间
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) {
lastId.value = null
lastTime.value = null
emailList.value = []
finished.value = false
}
loading.value = true
try {
const params = {}
if (lastTime.value && lastId.value) {
params.lastReceivedDate = lastTime.value
params.lastId = lastId.value
}
const response = await getEmailList(params)
if (response.success) {
const newList = response.data || []
total.value = response.total || 0
const newLastId = response.lastId || 0
const newLastTime = response.lastTime
if (isRefresh) {
emailList.value = newList
} else {
emailList.value = [...(emailList.value || []), ...newList]
}
// 更新游标
if (newLastId > 0 && newLastTime) {
lastId.value = newLastId
lastTime.value = newLastTime
}
// 判断是否还有更多数据返回数据少于20条或为空说明没有更多了
if (newList.length === 0 || newList.length < 20) {
finished.value = true
} else {
finished.value = false
}
} 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)
})
</script>
<style scoped>
@import '@/styles/common.css';
.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%;
overflow-y: auto;
}
.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%;
}
</style>