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 && !(transactionList && transactionList.length)" vertical style="padding: 50px 0">
|
|
|
|
|
|
加载中...
|
|
|
|
|
|
</van-loading>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 交易记录列表 -->
|
|
|
|
|
|
<TransactionList
|
|
|
|
|
|
:transactions="transactionList"
|
|
|
|
|
|
:loading="loading"
|
|
|
|
|
|
:finished="finished"
|
2026-01-01 11:58:21 +08:00
|
|
|
|
:show-delete="true"
|
2025-12-25 11:20:56 +08:00
|
|
|
|
@load="onLoad"
|
|
|
|
|
|
@click="viewDetail"
|
2026-01-01 11:58:21 +08:00
|
|
|
|
@delete="(id) => {
|
|
|
|
|
|
// 从当前的交易列表中移除该交易
|
|
|
|
|
|
transactionList.value = transactionList.value.filter(t => t.id !== id)
|
|
|
|
|
|
}"
|
2025-12-25 11:20:56 +08:00
|
|
|
|
/>
|
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>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 详情/编辑弹出层 -->
|
|
|
|
|
|
<TransactionDetail
|
|
|
|
|
|
v-model:show="detailVisible"
|
|
|
|
|
|
:transaction="currentTransaction"
|
|
|
|
|
|
@save="onDetailSave"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2026-01-01 14:43:43 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-12-25 11:20:56 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 底部浮动搜索框 -->
|
|
|
|
|
|
<div class="floating-search">
|
|
|
|
|
|
<van-search
|
|
|
|
|
|
v-model="searchKeyword"
|
2025-12-26 15:21:31 +08:00
|
|
|
|
placeholder="搜索交易摘要、来源、卡号、分类"
|
2025-12-25 11:20:56 +08:00
|
|
|
|
@update:model-value="onSearchChange"
|
|
|
|
|
|
@clear="onSearchClear"
|
|
|
|
|
|
shape="round"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2026-01-01 14:43:43 +08:00
|
|
|
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
2026-01-01 11:58:21 +08:00
|
|
|
|
import { showToast } from 'vant'
|
2025-12-25 11:20:56 +08:00
|
|
|
|
import {
|
|
|
|
|
|
getTransactionList,
|
2026-01-01 14:43:43 +08:00
|
|
|
|
getTransactionDetail
|
2025-12-25 11:20:56 +08:00
|
|
|
|
} from '@/api/transactionRecord'
|
|
|
|
|
|
import TransactionList from '@/components/TransactionList.vue'
|
|
|
|
|
|
import TransactionDetail from '@/components/TransactionDetail.vue'
|
|
|
|
|
|
|
|
|
|
|
|
const transactionList = 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 currentTransaction = ref(null)
|
|
|
|
|
|
|
|
|
|
|
|
// 搜索相关
|
|
|
|
|
|
const searchKeyword = ref('')
|
|
|
|
|
|
let searchTimer = null
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 加载数据
|
|
|
|
|
|
const loadData = async (isRefresh = false) => {
|
|
|
|
|
|
if (isRefresh) {
|
2025-12-27 22:05:50 +08:00
|
|
|
|
pageIndex.value = 1
|
2025-12-25 11:20:56 +08:00
|
|
|
|
transactionList.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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加搜索关键词
|
|
|
|
|
|
if (searchKeyword.value) {
|
|
|
|
|
|
params.searchKeyword = searchKeyword.value
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const response = await getTransactionList(params)
|
|
|
|
|
|
|
|
|
|
|
|
if (response.success) {
|
|
|
|
|
|
const newList = response.data || []
|
|
|
|
|
|
total.value = response.total || 0
|
|
|
|
|
|
|
|
|
|
|
|
if (isRefresh) {
|
|
|
|
|
|
transactionList.value = newList
|
|
|
|
|
|
} else {
|
|
|
|
|
|
transactionList.value = [...(transactionList.value || []), ...newList]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-27 22:05:50 +08:00
|
|
|
|
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 = () => {
|
|
|
|
|
|
finished.value = false
|
|
|
|
|
|
transactionList.value = []
|
2026-01-01 11:58:21 +08:00
|
|
|
|
loadData(true)
|
2025-12-25 11:20:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 搜索相关方法
|
|
|
|
|
|
const onSearchChange = () => {
|
|
|
|
|
|
// 防抖处理,用户停止输入500ms后自动搜索
|
|
|
|
|
|
if (searchTimer) {
|
|
|
|
|
|
clearTimeout(searchTimer)
|
|
|
|
|
|
}
|
|
|
|
|
|
searchTimer = setTimeout(() => {
|
|
|
|
|
|
onSearch()
|
|
|
|
|
|
}, 500)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const onSearch = () => {
|
|
|
|
|
|
// 重置分页状态并刷新数据
|
|
|
|
|
|
transactionList.value = []
|
|
|
|
|
|
finished.value = false
|
|
|
|
|
|
loadData(true)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const onSearchClear = () => {
|
|
|
|
|
|
searchKeyword.value = ''
|
|
|
|
|
|
onSearch()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载更多
|
|
|
|
|
|
const onLoad = () => {
|
|
|
|
|
|
loadData(false)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查看详情
|
|
|
|
|
|
const viewDetail = async (transaction) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await getTransactionDetail(transaction.id)
|
|
|
|
|
|
if (response.success) {
|
|
|
|
|
|
currentTransaction.value = response.data
|
|
|
|
|
|
detailVisible.value = true
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showToast(response.message || '获取详情失败')
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取详情出错:', error)
|
|
|
|
|
|
showToast('获取详情失败')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 详情保存后的回调
|
|
|
|
|
|
const onDetailSave = async () => {
|
|
|
|
|
|
loadData(true)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-01 11:58:21 +08:00
|
|
|
|
// 删除功能由 TransactionList 组件内部处理,组件通过 :show-delete 启用
|
2025-12-25 11:20:56 +08:00
|
|
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
|
// 不需要手动调用 loadData,van-list 会自动触发 onLoad
|
|
|
|
|
|
})
|
2025-12-28 10:23:57 +08:00
|
|
|
|
|
2026-01-01 11:58:21 +08:00
|
|
|
|
// 监听全局删除事件,保持页面一致性
|
2026-01-01 14:43:43 +08:00
|
|
|
|
const onGlobalTransactionDeleted = () => {
|
2026-01-01 11:58:21 +08:00
|
|
|
|
// 如果在此页面,重新刷新当前列表以保持数据一致
|
|
|
|
|
|
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)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 外部新增/修改/批量更新时的刷新监听
|
2026-01-01 14:43:43 +08:00
|
|
|
|
const onGlobalTransactionsChanged = () => {
|
2026-01-01 11:58:21 +08:00
|
|
|
|
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)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-01-01 14:43:43 +08:00
|
|
|
|
|
2025-12-28 10:23:57 +08:00
|
|
|
|
|
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
|
|
|
|
.floating-search {
|
|
|
|
|
|
position: fixed;
|
2025-12-25 17:28:06 +08:00
|
|
|
|
bottom: 90px;
|
2025-12-25 11:20:56 +08:00
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
z-index: 999;
|
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.floating-search :deep(.van-search) {
|
|
|
|
|
|
pointer-events: auto;
|
|
|
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
|
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-12-26 18:03:52 +08:00
|
|
|
|
|
|
|
|
|
|
/* 设置页面容器背景色 */
|
|
|
|
|
|
:deep(.van-nav-bar) {
|
|
|
|
|
|
background: transparent !important;
|
|
|
|
|
|
}
|
2025-12-25 11:20:56 +08:00
|
|
|
|
</style>
|