优化账户余额调整功能:新增调整余额的逻辑,更新相关页面以显示账户余额和可用额度,调整路由配置以移除不必要的动画效果。

This commit is contained in:
2026-04-05 18:40:52 +08:00
parent 5fbff39c4f
commit c7c84c74d3
9 changed files with 255 additions and 84 deletions
@@ -131,7 +131,7 @@
</v-col>
<v-col cols="12" :md="(!editAccountId || isNewAccount(selectedAccount)) && selectedAccount.balance ? 6 : 12"
v-if="account.type === AccountType.SingleAccount.type || currentAccountIndex >= 0">
<amount-input :disabled="loading || submitting || (!!editAccountId && !isNewAccount(selectedAccount))"
<amount-input :disabled="loading || submitting"
:persistent-placeholder="true"
:currency="selectedAccount.currency"
:show-currency="true"
@@ -204,6 +204,9 @@ import { useAccountEditPageBase } from '@/views/base/accounts/AccountEditPageBas
import { useUserStore } from '@/stores/user.ts';
import { useAccountsStore } from '@/stores/account.ts';
import { useTransactionsStore } from '@/stores/transaction.ts';
import { KnownErrorCode } from '@/consts/api.ts';
import { itemAndIndex } from '@/core/base.ts';
import { AccountType } from '@/core/account.ts';
@@ -254,6 +257,7 @@ const {
const userStore = useUserStore();
const accountsStore = useAccountsStore();
const transactionsStore = useTransactionsStore();
const confirmDialog = useTemplateRef<ConfirmDialogType>('confirmDialog');
const snackbar = useTemplateRef<SnackBarType>('snackbar');
@@ -261,6 +265,7 @@ const snackbar = useTemplateRef<SnackBarType>('snackbar');
const showState = ref<boolean>(false);
const activeTab = ref<string>('account');
const currentAccountIndex = ref<number>(-1);
const originalBalances = ref<Map<string, number>>(new Map());
const selectedAccount = computed<Account>(() => {
if (currentAccountIndex.value < 0) {
@@ -310,6 +315,15 @@ function open(options?: { id?: string, currentAccount?: Account, category?: numb
accountId: editAccountId.value
}).then(response => {
setAccount(response);
originalBalances.value.clear();
if (account.value.id) {
originalBalances.value.set(account.value.id, account.value.balance);
}
for (const subAccount of subAccounts.value) {
if (subAccount.id) {
originalBalances.value.set(subAccount.id, subAccount.balance);
}
}
loading.value = false;
}).catch(error => {
loading.value = false;
@@ -337,7 +351,7 @@ function open(options?: { id?: string, currentAccount?: Account, category?: numb
});
}
function save(): void {
async function save(): Promise<void> {
const problemMessage = inputEmptyProblemMessage.value;
if (problemMessage) {
@@ -347,6 +361,30 @@ function save(): void {
submitting.value = true;
// Collect balance adjustments needed for existing accounts
let balanceChanged = false;
if (editAccountId.value) {
const allAccounts = [account.value, ...subAccounts.value];
for (const acc of allAccounts) {
if (!acc.id || isNewAccount(acc)) continue;
const origBalance = originalBalances.value.get(acc.id);
if (origBalance !== undefined && acc.balance !== origBalance) {
balanceChanged = true;
try {
await transactionsStore.adjustAccountBalance({
accountId: acc.id,
targetBalance: acc.balance,
currentBalance: origBalance
});
} catch (error: unknown) {
submitting.value = false;
snackbar.value?.showError(error as { message: string });
return;
}
}
}
}
accountsStore.saveAccount({
account: account.value,
subAccounts: subAccounts.value,
@@ -366,6 +404,12 @@ function save(): void {
}).catch(error => {
submitting.value = false;
if (balanceChanged && error.error && error.error.errorCode === KnownErrorCode.NothingWillBeUpdated) {
resolveFunc?.({ message: 'You have saved this account' });
showState.value = false;
return;
}
if (!error.processed) {
snackbar.value?.showError(error);
}
@@ -211,6 +211,7 @@
:disabled="loading || submitting || !allVisibleAccounts.length || (mode === TransactionEditPageMode.Edit && transaction.type === TransactionType.ModifyBalance)"
:enable-filter="true" :filter-placeholder="tt('Find account')" :filter-no-items-text="tt('No available account')"
:custom-selection-primary-text="sourceAccountName"
:custom-selection-secondary-text="sourceAccountBalanceDisplay"
:label="tt(sourceAccountTitle)"
:placeholder="tt(sourceAccountTitle)"
:items="allVisibleCategorizedAccounts"
@@ -236,6 +237,7 @@
:disabled="loading || submitting || !allVisibleAccounts.length"
:enable-filter="true" :filter-placeholder="tt('Find account')" :filter-no-items-text="tt('No available account')"
:custom-selection-primary-text="destinationAccountName"
:custom-selection-secondary-text="destinationAccountBalanceDisplay"
:label="tt('Destination Account')"
:placeholder="tt('Destination Account')"
:items="allVisibleCategorizedAccounts"
@@ -510,6 +512,7 @@ import { SUPPORTED_IMAGE_EXTENSIONS } from '@/consts/file.ts';
import { TransactionTemplate } from '@/models/transaction_template.ts';
import type { TransactionPictureInfoBasicResponse } from '@/models/transaction_picture_info.ts';
import { Transaction } from '@/models/transaction.ts';
import type { Account } from '@/models/account.ts';
import {
getTimezoneOffsetMinutes,
@@ -567,7 +570,7 @@ const props = defineProps<{
show?: boolean;
}>();
const { tt } = useI18n();
const { tt, formatAmountToLocalizedNumeralsWithCurrency } = useI18n();
const {
mode,
@@ -644,6 +647,31 @@ const initOptions = ref<TransactionEditOptions | undefined>(undefined);
let resolveFunc: ((response?: TransactionEditResponse) => void) | null = null;
let rejectFunc: ((reason?: unknown) => void) | null = null;
function getAccountBalanceDisplay(account: Account): string {
if (account.creditLimit) {
const outstanding = -account.balance;
const available = account.creditLimit + account.balance;
return formatAmountToLocalizedNumeralsWithCurrency(outstanding, account.currency)
+ ' · ' + tt('Available') + ' ' + formatAmountToLocalizedNumeralsWithCurrency(available, account.currency);
}
const displayBalance = account.isLiability ? -account.balance : account.balance;
return formatAmountToLocalizedNumeralsWithCurrency(displayBalance, account.currency);
}
const sourceAccountBalanceDisplay = computed<string>(() => {
if (!transaction.value.sourceAccountId) return '';
const account = allVisibleAccounts.value.find(a => a.id === transaction.value.sourceAccountId);
if (!account) return '';
return getAccountBalanceDisplay(account);
});
const destinationAccountBalanceDisplay = computed<string>(() => {
if (!transaction.value.destinationAccountId) return '';
const account = allVisibleAccounts.value.find(a => a.id === transaction.value.destinationAccountId);
if (!account) return '';
return getAccountBalanceDisplay(account);
});
const sourceAmountColor = computed<string | undefined>(() => {
if (transaction.value.type === TransactionType.Expense) {
return 'expense';