优化待分类页面
Some checks failed
Docker Build & Deploy / Build Docker Image (push) Failing after 1m55s
Docker Build & Deploy / Deploy to Production (push) Has been skipped
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s

This commit is contained in:
SunCheng
2026-01-27 16:46:16 +08:00
parent 4aa7e82429
commit b78774bc39
2 changed files with 197 additions and 34 deletions

View File

@@ -60,10 +60,15 @@ public decimal Amount { get; set; }
/// </summary>
public string ImportNo { get; set; } = string.Empty;
/// <summary>
/// <summary>
/// 导入来源
/// </summary>
public string ImportFrom { get; set; } = string.Empty;
/// <summary>
/// 退款金额
/// </summary>
public decimal RefundAmount { get; set; }
}
public enum TransactionType

View File

@@ -1,4 +1,4 @@
<template>
<template>
<div class="page-container-flex unconfirmed-classification">
<van-nav-bar
title="待确认分类"
@@ -29,17 +29,63 @@
</van-loading>
</div>
<TransactionList
<div
v-else-if="treeData.length === 0 && !loading"
class="empty-container"
>
<van-empty description="暂无待确认分类" />
</div>
<van-collapse
v-else
:transactions="displayTransactions"
:loading="loading"
:finished="true"
show-checkbox
:selected-ids="selectedIds"
@click="handleTransactionClick"
@delete="handleTransactionDeleted"
@update:selected-ids="updateSelectedIds"
/>
v-model="activeNames"
:border="false"
>
<van-collapse-item
v-for="typeNode in treeData"
:key="typeNode.id"
:name="typeNode.id"
class="type-node"
>
<template #title>
<div class="node-title">
<span class="node-count">{{ typeNode.count }}</span>
<span class="node-name">{{ typeNode.text }}</span>
<span class="node-amount">{{ formatAmount(typeNode.amount) }}</span>
</div>
</template>
<van-collapse
v-model="activeClassifyNames"
:border="false"
class="classify-collapse"
>
<van-collapse-item
v-for="classifyNode in typeNode.children"
:key="classifyNode.id"
:name="classifyNode.id"
class="classify-node"
>
<template #title>
<div class="node-title">
<span class="node-count">{{ classifyNode.count }}</span>
<span class="node-name">{{ classifyNode.text }}</span>
<span class="node-amount">{{ formatAmount(classifyNode.amount) }}</span>
</div>
</template>
<TransactionList
:transactions="classifyNode.children.map(c => c.transaction)"
:show-delete="false"
:show-checkbox="true"
:selected-ids="selectedIds"
@click="handleTransactionClick"
@update:selected-ids="handleUpdateSelectedIds"
/>
</van-collapse-item>
</van-collapse>
</van-collapse-item>
</van-collapse>
</div>
<!-- 交易详情弹窗 -->
@@ -56,8 +102,8 @@ import { ref, onMounted, computed } from 'vue'
import { useRouter } from 'vue-router'
import { showToast, showConfirmDialog } from 'vant'
import { getUnconfirmedTransactionList, confirmAllUnconfirmed } from '@/api/transactionRecord'
import TransactionList from '@/components/TransactionList.vue'
import TransactionDetail from '@/components/TransactionDetail.vue'
import TransactionList from '@/components/TransactionList.vue'
const router = useRouter()
const loading = ref(false)
@@ -66,6 +112,14 @@ const transactions = ref([])
const showDetail = ref(false)
const currentTransaction = ref(null)
const selectedIds = ref(new Set())
const activeNames = ref([])
const activeClassifyNames = ref([])
const TYPE_NAMES = {
0: '支出',
1: '收入',
2: '不记收支'
}
const onClickLeft = () => {
if (window.history.length > 1) {
@@ -79,7 +133,7 @@ const handleConfirmSelected = async () => {
try {
await showConfirmDialog({
title: '提示',
message: `确定要将这 ${transactions.value.length} 条记录的所有建议分类转为正式分类吗?`
message: `确定要将这 ${selectedIds.value.size} 条记录的所有建议分类转为正式分类吗?`
})
confirming.value = true
@@ -99,13 +153,76 @@ const handleConfirmSelected = async () => {
}
}
// 转换数据格式以适配 TransactionList 组件
const displayTransactions = computed(() => {
return transactions.value.map((t) => ({
...t,
upsetedClassify: t.unconfirmedClassify,
upsetedType: t.unconfirmedType
}))
const formatAmount = (amount) => {
if (amount === null || amount === undefined) {return ''}
const num = parseFloat(amount)
if (isNaN(num)) {return ''}
return num.toFixed(2)
}
const buildTreeData = (data) => {
const typeMap = {}
data.forEach((item) => {
const type = item.unconfirmedType ?? item.type
const classify = item.unconfirmedClassify ?? item.classify
const typeName = TYPE_NAMES[type] || '未分类'
const classifyName = classify || '未分类'
if (!typeMap[typeName]) {
typeMap[typeName] = {
id: `type-${type}`,
text: typeName,
type: 'type',
typeId: type,
children: [],
count: 0,
amount: 0
}
}
let classifyNode = typeMap[typeName].children.find((c) => c.text === classifyName)
if (!classifyNode) {
classifyNode = {
id: `classify-${type}-${classifyName}`,
text: classifyName,
type: 'classify',
typeId: type,
classify: classifyName,
children: [],
count: 0,
amount: 0
}
typeMap[typeName].children.push(classifyNode)
}
classifyNode.children.push({
id: item.id,
text: item.reason || item.occurredAt,
type: 'transaction',
transaction: item
})
classifyNode.count += 1
classifyNode.amount += parseFloat(item.amount) || 0
typeMap[typeName].count += 1
typeMap[typeName].amount += parseFloat(item.amount) || 0
})
return Object.values(typeMap).map((typeNode) => {
typeNode.children.forEach((classifyNode) => {
classifyNode.children.sort((a, b) => {
const dateA = new Date(a.transaction.occurredAt)
const dateB = new Date(b.transaction.occurredAt)
return dateB - dateA
})
})
return typeNode
})
}
const treeData = computed(() => {
return buildTreeData(transactions.value)
})
const loadData = async () => {
@@ -113,12 +230,9 @@ const loadData = async () => {
try {
const response = await getUnconfirmedTransactionList()
if (response && response.success) {
transactions.value = (response.data || []).map((t) => ({
...t,
upsetedClassify: t.unconfirmedClassify,
upsetedType: t.unconfirmedType
}))
transactions.value = response.data || []
selectedIds.value = new Set(response.data.map((t) => t.id))
activeNames.value = treeData.value.map((node) => node.id)
}
} catch (error) {
console.error('获取待确认列表失败:', error)
@@ -132,12 +246,8 @@ const handleTransactionClick = (transaction) => {
showDetail.value = true
}
const handleTransactionDeleted = (id) => {
transactions.value = transactions.value.filter((t) => t.id !== id)
}
const updateSelectedIds = (ids) => {
selectedIds.value = new Set(ids)
const handleUpdateSelectedIds = (newSelectedIds) => {
selectedIds.value = newSelectedIds
}
const handleDetailSave = () => {
@@ -159,17 +269,65 @@ onMounted(() => {
.scroll-content {
flex: 1;
overflow-y: auto;
padding: 12px;
}
.loading-container {
.loading-container,
.empty-container {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
/* 设置页面容器背景色 */
.node-title {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
}
.node-name {
flex: 1;
font-weight: 500;
}
.node-count {
font-size: 12px;
color: #fff;
background: var(--van-primary-color);
padding: 2px 6px;
border-radius: 4px;
}
.node-amount {
font-size: 14px;
font-weight: 600;
color: var(--van-orange);
}
.type-node :deep(.van-collapse-item__title) {
font-size: 16px;
font-weight: 600;
}
.classify-node :deep(.van-collapse-item__title) {
font-size: 14px;
}
.classify-collapse {
padding-left: 8px;
}
.classify-collapse :deep(.van-cell-group--inset) {
margin-left: -8px;
}
:deep(.van-nav-bar) {
background: transparent !important;
}
:deep(.van-cell) {
padding: 8px 12px;
}
</style>