fix
Some checks failed
Docker Build & Deploy / Build Docker Image (push) Failing after 6s
Docker Build & Deploy / Deploy to Production (push) Has been skipped

This commit is contained in:
2026-01-01 11:58:21 +08:00
parent 0444218898
commit c1aa4df4f3
11 changed files with 442 additions and 165 deletions

View File

@@ -31,6 +31,7 @@
:finished="true"
:show-delete="true"
@click="viewDetail"
@delete="handleDateTransactionDelete"
/>
</PopupContainer>
@@ -44,7 +45,7 @@
</template>
<script setup>
import { ref, onMounted, nextTick } from "vue";
import { ref, onMounted, nextTick, onBeforeUnmount } from "vue";
import { showToast } from "vant";
import request from "@/api/request";
import { getTransactionDetail, getTransactionsByDate } from "@/api/transactionRecord";
@@ -214,6 +215,15 @@ const onDetailSave = async (saveData) => {
fetchDailyStatistics(now.getFullYear(), now.getMonth() + 1);
};
// 处理删除事件:从当前日期交易列表中移除,并刷新当日和当月统计
const handleDateTransactionDelete = async (transactionId) => {
dateTransactions.value = dateTransactions.value.filter(t => t.id !== transactionId)
// 刷新当前日期以及当月的统计数据
const now = selectedDate.value || new Date();
fetchDailyStatistics(now.getFullYear(), now.getMonth() + 1);
};
// 智能分类保存回调
const onSmartClassifySave = async () => {
// 保存完成后重新加载数据
@@ -248,6 +258,36 @@ const formatterCalendar = (day) => {
// 初始加载当前月份数据
const now = new Date();
fetchDailyStatistics(now.getFullYear(), now.getMonth() + 1);
// 全局删除事件监听,确保日历页面数据一致
const onGlobalTransactionDeleted = (e) => {
if (selectedDate.value) {
fetchDateTransactions(selectedDate.value)
}
const now = selectedDate.value || new Date()
fetchDailyStatistics(now.getFullYear(), now.getMonth() + 1)
}
window.addEventListener && window.addEventListener('transaction-deleted', onGlobalTransactionDeleted)
onBeforeUnmount(() => {
window.removeEventListener && window.removeEventListener('transaction-deleted', onGlobalTransactionDeleted)
})
// 当有交易被新增/修改/批量更新时刷新
const onGlobalTransactionsChanged = (e) => {
if (selectedDate.value) {
fetchDateTransactions(selectedDate.value)
}
const now = selectedDate.value || new Date()
fetchDailyStatistics(now.getFullYear(), now.getMonth() + 1)
}
window.addEventListener && window.addEventListener('transactions-changed', onGlobalTransactionsChanged)
onBeforeUnmount(() => {
window.removeEventListener && window.removeEventListener('transactions-changed', onGlobalTransactionsChanged)
})
</script>
<style scoped>

View File

@@ -50,6 +50,7 @@ const error = ref(false)
const finished = ref(false)
const hasData = ref(false)
const unclassifiedCount = ref(0)
const _loadedUnclassifiedInitially = ref(false)
// 获取未分类账单统计
const loadUnclassifiedCount = async () => {
@@ -72,6 +73,10 @@ const handleDataLoaded = ({ groups, total, finished: isFinished }) => {
// 处理数据变更
const handleDataChanged = async () => {
await loadUnclassifiedCount()
// 重新加载数据
listLoading.value = true
await onLoad()
listLoading.value = false
}
// 加载更多
@@ -83,14 +88,16 @@ const onLoad = async () => {
try {
await groupListRef.value.loadData()
// 首次加载时获取统计信息
if (!hasData.value) {
// 首次加载时获取统计信息(确保统计不会丢失)
if (!_loadedUnclassifiedInitially.value) {
await loadUnclassifiedCount()
_loadedUnclassifiedInitially.value = true
}
error.value = false
} catch (err) {
console.error('加载分组数据失败:', err)
error.value = true
} finally {
listLoading.value = false
@@ -106,6 +113,13 @@ const handleBack = () => {
onMounted(async () => {
// 初始加载数据
listLoading.value = true
try {
// 先确保统计加载,避免统计消失
await loadUnclassifiedCount()
_loadedUnclassifiedInitially.value = true
} catch (e) {
console.error('初始化加载未分类统计失败:', e)
}
await onLoad()
})
</script>

View File

@@ -51,7 +51,7 @@
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { ref, computed, onMounted, nextTick } from 'vue'
import { useRouter } from 'vue-router'
import { showToast, showLoadingToast, closeToast, showConfirmDialog } from 'vant'
import {
@@ -68,6 +68,7 @@ const totalGroups = ref(0)
const classifying = ref(false)
const hasChanges = ref(false)
const classifyBuffer = ref('')
const suppressDataChanged = ref(false)
// 计算已选中的数量
const selectedCount = computed(() => {
@@ -98,6 +99,11 @@ const handleDataLoaded = ({ groups, total }) => {
// 处理数据变更
const handleDataChanged = async () => {
if (suppressDataChanged.value) {
suppressDataChanged.value = false
return
}
await loadUnclassifiedCount()
hasChanges.value = false
}
@@ -191,6 +197,7 @@ const startClassify = async () => {
// 处理SSE事件
const handleSSEEvent = (eventType, data, classifyResults) => {
console.log('收到事件:', eventType, data)
if (eventType === 'data') {
try {
classifyBuffer.value += data
@@ -221,27 +228,32 @@ const handleSSEEvent = (eventType, data, classifyResults) => {
try {
const result = JSON.parse(jsonStr)
if (result.id && groupListRef.value) {
classifyResults.set(result.id, {
classify: result.classify || '',
type: result.type !== undefined ? result.type : null
classify: result.Classify || '',
type: result.Type !== undefined ? result.Type : null
})
// 更新组件内的分组显示状态
const groups = groupListRef.value.getList()
for (const group of groups) {
if (group.transactionIds.includes(result.id)) {
group.sampleClassify = result.classify || ''
if (result.type !== undefined && result.type !== null) {
group.sampleType = result.type
group.sampleClassify = result.Classify || ''
if (result.Type !== undefined && result.Type !== null) {
group.sampleType = result.Type
}
hasChanges.value = true
break
}
}
// 更新回组件
// 更新回组件(内部更新时抑制 data-changed 处理)
suppressDataChanged.value = true
groupListRef.value.setList(groups)
// 确保在子组件内部事件触发后恢复标志并保持 hasChanges
nextTick(() => {
suppressDataChanged.value = false
hasChanges.value = true
})
}
} catch (e) {
console.error('JSON解析失败:', e)

View File

@@ -129,6 +129,7 @@
:finished="true"
:show-delete="true"
@click="handleTransactionClick"
@delete="handleTransactionDelete"
/>
</PopupContainer>
@@ -143,7 +144,7 @@
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { showToast, showConfirmDialog } from 'vant'
import { getEmailList, getEmailDetail, deleteEmail, refreshTransactionRecords, syncEmails, getEmailTransactions } from '@/api/emailRecord'
import { getTransactionDetail } from '@/api/transactionRecord'
@@ -281,7 +282,7 @@ const handleRefreshAnalysis = async () => {
})
refreshingAnalysis.value = true
const response = await refreshTransactionRecords(currentEmail.value.id || currentEmail.value.Id)
const response = await refreshTransactionRecords(currentEmail.value.id)
if (response.success) {
showToast('重新分析成功')
@@ -325,7 +326,7 @@ const viewTransactions = async () => {
if (!currentEmail.value) return
try {
const emailId = currentEmail.value.id || currentEmail.value.Id
const emailId = currentEmail.value.id
const response = await getEmailTransactions(emailId)
if (response.success) {
@@ -340,6 +341,46 @@ const viewTransactions = async () => {
}
}
// 监听全局删除事件,保持弹窗内交易列表一致
const onGlobalTransactionDeleted = (e) => {
// 如果交易列表弹窗打开,尝试重新加载邮箱的交易列表
if (transactionListVisible.value && currentEmail.value) {
const emailId = currentEmail.value.id || currentEmail.value.Id
getEmailTransactions(emailId).then(response => {
if (response.success) {
transactionList.value = response.data || []
}
}).catch(() => {})
}
}
window.addEventListener && window.addEventListener('transaction-deleted', onGlobalTransactionDeleted)
onBeforeUnmount(() => {
window.removeEventListener && window.removeEventListener('transaction-deleted', onGlobalTransactionDeleted)
})
// 监听新增/修改/批量更新事件,刷新弹窗内交易或邮件列表
const onGlobalTransactionsChanged = (e) => {
if (transactionListVisible.value && currentEmail.value) {
const emailId = currentEmail.value.id || currentEmail.value.Id
getEmailTransactions(emailId).then(response => {
if (response.success) {
transactionList.value = response.data || []
}
}).catch(() => {})
} else {
// 也刷新邮件列表以保持统计一致
loadData(true)
}
}
window.addEventListener && window.addEventListener('transactions-changed', onGlobalTransactionsChanged)
onBeforeUnmount(() => {
window.removeEventListener && window.removeEventListener('transactions-changed', onGlobalTransactionsChanged)
})
// 处理点击账单
const handleTransactionClick = async (transaction) => {
try {
@@ -356,16 +397,36 @@ const handleTransactionClick = async (transaction) => {
}
}
const handleTransactionDelete = (transactionId) => {
// 从当前的交易列表中移除该交易
transactionList.value = transactionList.value.filter(t => t.id !== transactionId)
// 刷新邮件列表
loadData(true)
// 刷新当前邮件详情
if (currentEmail.value) {
const emailId = currentEmail.value.id
getEmailDetail(emailId).then(response => {
if (response.success) {
currentEmail.value = response.data
}
})
}
try { window.dispatchEvent(new CustomEvent('transaction-deleted', { detail: transactionId })) } catch(e) {}
}
// 账单保存后刷新列表
const handleTransactionSave = async () => {
// 刷新账单列表
if (currentEmail.value) {
const emailId = currentEmail.value.id || currentEmail.value.Id
const emailId = currentEmail.value.id
const response = await getEmailTransactions(emailId)
if (response.success) {
transactionList.value = response.data || []
}
}
try { window.dispatchEvent(new CustomEvent('transactions-changed', { detail: { emailId: currentEmail.value?.id } })) } catch(e) {}
}
// 格式化日期

View File

@@ -301,6 +301,7 @@
:show-delete="true"
@load="loadCategoryBills"
@click="viewBillDetail"
@delete="handleCategoryBillsDelete"
/>
</PopupContainer>
@@ -315,6 +316,7 @@
<script setup>
import { ref, computed, onMounted, onActivated, nextTick } from 'vue'
import { onBeforeUnmount } from 'vue'
import { showToast } from 'vant'
import { useRouter } from 'vue-router'
import { getMonthlyStatistics, getCategoryStatistics, getTrendStatistics } from '@/api/statistics'
@@ -715,6 +717,14 @@ const viewBillDetail = async (transaction) => {
}
}
const handleCategoryBillsDelete = (deletedId) => {
categoryBills.value = categoryBills.value.filter(t => t.id !== deletedId)
categoryBillsTotal.value--
// 被删除后刷新统计数据和账单列表
fetchStatistics()
}
// 账单保存后的回调
const onBillSave = async () => {
// 刷新统计数据
@@ -746,6 +756,7 @@ const onSmartClassifySave = async () => {
// 刷新统计数据
await fetchStatistics()
try { window.dispatchEvent(new CustomEvent('transactions-changed', { detail: { reason: selectedClassify.value, type: selectedType.value } })) } catch(e) {}
showToast('智能分类已保存')
}
@@ -778,6 +789,28 @@ onMounted(() => {
onActivated(() => {
fetchStatistics()
})
// 全局监听交易删除事件,确保统计数据一致
const onGlobalTransactionDeleted = (e) => {
// e.detail contains transaction id
fetchStatistics()
}
window.addEventListener && window.addEventListener('transaction-deleted', onGlobalTransactionDeleted)
onBeforeUnmount(() => {
window.removeEventListener && window.removeEventListener('transaction-deleted', onGlobalTransactionDeleted)
})
const onGlobalTransactionsChanged = (e) => {
fetchStatistics()
}
window.addEventListener && window.addEventListener('transactions-changed', onGlobalTransactionsChanged)
onBeforeUnmount(() => {
window.removeEventListener && window.removeEventListener('transactions-changed', onGlobalTransactionsChanged)
})
</script>
<style scoped>

View File

@@ -12,9 +12,13 @@
:transactions="transactionList"
:loading="loading"
:finished="finished"
:show-delete="true"
@load="onLoad"
@click="viewDetail"
@delete="handleDelete"
@delete="(id) => {
// 从当前的交易列表中移除该交易
transactionList.value = transactionList.value.filter(t => t.id !== id)
}"
/>
<!-- 底部安全距离 -->
@@ -161,8 +165,8 @@
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { showToast, showConfirmDialog } from 'vant'
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
import { showToast } from 'vant'
import {
getTransactionList,
getTransactionDetail,
@@ -291,7 +295,7 @@ const loadData = async (isRefresh = false) => {
const onRefresh = () => {
finished.value = false
transactionList.value = []
loadData(false)
loadData(true)
}
// 搜索相关方法
@@ -345,28 +349,7 @@ const onDetailSave = async () => {
await loadClassifyList()
}
// 删除
const handleDelete = async (transaction) => {
try {
await showConfirmDialog({
title: '提示',
message: '确定要删除这条交易记录吗?',
})
const response = await deleteTransaction(transaction.id)
if (response.success) {
showToast('删除成功')
loadData(true)
} else {
showToast(response.message || '删除失败')
}
} catch (error) {
if (error !== 'cancel') {
console.error('删除出错:', error)
showToast('删除失败')
}
}
}
// 删除功能由 TransactionList 组件内部处理,组件通过 :show-delete 启用
// 打开新增弹窗
const openAddDialog = () => {
@@ -484,6 +467,7 @@ const onAddSubmit = async () => {
showToast('添加成功')
addDialogVisible.value = false
loadData(true)
try { window.dispatchEvent(new CustomEvent('transactions-changed', { detail: response.data })) } catch(e) {}
// 重新加载分类列表
await loadClassifyList()
} else {
@@ -502,6 +486,35 @@ onMounted(async () => {
// 不需要手动调用 loadDatavan-list 会自动触发 onLoad
})
// 监听全局删除事件,保持页面一致性
const onGlobalTransactionDeleted = (e) => {
// 如果在此页面,重新刷新当前列表以保持数据一致
transactionList.value = []
pageIndex.value = 1
finished.value = false
loadData(true)
}
window.addEventListener && window.addEventListener('transaction-deleted', onGlobalTransactionDeleted)
onBeforeUnmount(() => {
window.removeEventListener && window.removeEventListener('transaction-deleted', onGlobalTransactionDeleted)
})
// 外部新增/修改/批量更新时的刷新监听
const onGlobalTransactionsChanged = (e) => {
transactionList.value = []
pageIndex.value = 1
finished.value = false
loadData(true)
}
window.addEventListener && window.addEventListener('transactions-changed', onGlobalTransactionsChanged)
onBeforeUnmount(() => {
window.removeEventListener && window.removeEventListener('transactions-changed', onGlobalTransactionsChanged)
})
// 暴露给父级方法调用
defineExpose({
openAddDialog