在交易列表页面顶部显示账户信息:新增账户图标、名称和余额信息卡片,优化单账户筛选时的显示逻辑

This commit is contained in:
2026-04-05 18:51:27 +08:00
parent c7c84c74d3
commit ba85852543
3 changed files with 72 additions and 15 deletions
+12 -13
View File
@@ -33,14 +33,15 @@
- 移动端 ListPage:账户列表显示「可用额度」(= `creditLimit + balance`,因信用卡余额为负值) - 移动端 ListPage:账户列表显示「可用额度」(= `creditLimit + balance`,因信用卡余额为负值)
- 语言包:中英文均已添加 "Credit Limit" / "Available" - 语言包:中英文均已添加 "Credit Limit" / "Available"
### 3. ⚠️ 账户交易列表顶部显示账户信息 ### 3. 🟢 账户交易列表顶部显示账户信息
**描述:** 在按账户筛选的交易列表页面顶部,显示账户名称余额(或信用卡欠款+额度)等信息卡片。 **描述:** 在按单个账户筛选的交易列表页面顶部,显示账户图标、名称余额信息卡片。
**实现思路** **已完成**
- 检测当前是否为单账户筛选状态 - 仅单账户筛选时(`queryAllFilterAccountIdsCount === 1`)显示,多账户/全部时不显示
- 顶部插入账户信息卡片组件 - 信用卡账户显示「欠款 · 可用 xxx」,普通账户显示余额
- 若多账户筛选则显示多张卡片 - 移动端:toolbar 下方插入 `f7-card`
- 涉及文件:`src/views/mobile/transactions/ListPage.vue` - 桌面端:日期范围行下方插入 `v-card`tonal 样式)
- 涉及文件:`src/views/mobile/transactions/ListPage.vue``src/views/desktop/transactions/ListPage.vue`
### 4. 🟢 允许直接修改账户余额(自动插入调整记录) ### 4. 🟢 允许直接修改账户余额(自动插入调整记录)
**描述:** 在账户编辑页直接修改余额字段,保存时自动计算差值并插入一条「余额调整」类型交易记录。 **描述:** 在账户编辑页直接修改余额字段,保存时自动计算差值并插入一条「余额调整」类型交易记录。
@@ -132,12 +133,10 @@
- Tab 切换动画保持原样(设置中已有开关可控制) - Tab 切换动画保持原样(设置中已有开关可控制)
- 涉及文件:`src/styles/mobile/global.scss` - 涉及文件:`src/styles/mobile/global.scss`
### 11. 🟢 点击响应卡顿优化 ### 11. 🔍 点击响应卡顿优化(暂调查)
**描述:** 点击按钮有延迟感,点不上的问题。 **描述:** 点击按钮有延迟感,点不上的问题。
**已完成** **初步判断** 可能是接口响应慢导致,非前端交互延迟问题。等离线缓存(#12)完成后再评估是否仍需处理。
- `tapHoldDelay` 从默认 750ms 降至 500ms,减少长按判定等待
- 涉及文件:`src/MobileApp.vue`
--- ---
@@ -163,7 +162,7 @@
|---|------|------| |---|------|------|
| 1 | 自选主题色 | ⚠️ 待做 | | 1 | 自选主题色 | ⚠️ 待做 |
| 2 | 信用卡额度 | 🟢 已完成 | | 2 | 信用卡额度 | 🟢 已完成 |
| 3 | 账户信息卡片 | ⚠️ 待做 | | 3 | 账户信息卡片 | 🟢 已完成 |
| 4 | 调整余额入口 | 🟢 已完成 | | 4 | 调整余额入口 | 🟢 已完成 |
| 5 | 记账页显示余额 | 🟢 已完成 | | 5 | 记账页显示余额 | 🟢 已完成 |
| 6 | 记住上次账户 | ❓ 待定 | | 6 | 记住上次账户 | ❓ 待定 |
@@ -171,5 +170,5 @@
| 8 | 详情编辑/删除 | 🟢 已完成 | | 8 | 详情编辑/删除 | 🟢 已完成 |
| 9 | 分类默认展开 | ❓ 待定 | | 9 | 分类默认展开 | ❓ 待定 |
| 10 | 全局动画加速 | 🟢 已完成 | | 10 | 全局动画加速 | 🟢 已完成 |
| 11 | 点击卡顿优化 | 🟢 已完成 | | 11 | 点击卡顿优化 | 🔍 暂调查 |
| 12 | 离线缓存 | ❌ 暂缓 | | 12 | 离线缓存 | ❌ 暂缓 |
+30 -1
View File
@@ -172,6 +172,16 @@
</div> </div>
</v-card-text> </v-card-text>
<v-card-text class="pt-0" v-if="filteredSingleAccount">
<v-card variant="tonal" class="d-inline-flex align-center px-4 py-2" rounded="lg">
<ItemIcon icon-type="account" :icon-id="filteredSingleAccount.icon" :color="filteredSingleAccount.color" />
<div class="ms-3">
<div class="text-subtitle-1 font-weight-bold">{{ filteredSingleAccount.name }}</div>
<div class="text-body-2 text-medium-emphasis">{{ filteredAccountBalanceText }}</div>
</div>
</v-card>
</v-card-text>
<v-card-text class="transaction-calendar-container pt-0" v-if="pageType === TransactionListPageType.Calendar.type"> <v-card-text class="transaction-calendar-container pt-0" v-if="pageType === TransactionListPageType.Calendar.type">
<transaction-calendar day-has-transaction-class="font-weight-bold" <transaction-calendar day-has-transaction-class="font-weight-bold"
:readonly="loading" :is-dark-mode="isDarkMode" :readonly="loading" :is-dark-mode="isDarkMode"
@@ -775,7 +785,8 @@ const {
tt, tt,
getAllRecentMonthDateRanges, getAllRecentMonthDateRanges,
getWeekdayLongName, getWeekdayLongName,
getCurrentNumeralSystemType getCurrentNumeralSystemType,
formatAmountToLocalizedNumeralsWithCurrency
} = useI18n(); } = useI18n();
const { const {
@@ -875,6 +886,24 @@ const showFilterTagDialog = ref<boolean>(false);
const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark); const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark);
const numeralSystem = computed<NumeralSystem>(() => getCurrentNumeralSystemType()); const numeralSystem = computed<NumeralSystem>(() => getCurrentNumeralSystemType());
const filteredSingleAccount = computed(() => {
if (queryAllFilterAccountIdsCount.value !== 1) return null;
return allAccountsMap.value[query.value.accountIds] ?? null;
});
const filteredAccountBalanceText = computed<string>(() => {
const account = filteredSingleAccount.value;
if (!account) return '';
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 allPageCounts = computed<NameNumeralValue[]>(() => { const allPageCounts = computed<NameNumeralValue[]>(() => {
const pageCounts: NameNumeralValue[] = []; const pageCounts: NameNumeralValue[] = [];
const availableCountPerPage: number[] = [ 5, 10, 15, 20, 25, 30, 50 ]; const availableCountPerPage: number[] = [ 5, 10, 15, 20, 25, 30, 50 ];
+30 -1
View File
@@ -69,6 +69,16 @@
</f7-link> </f7-link>
</f7-toolbar> </f7-toolbar>
<f7-card class="margin-vertical" v-if="filteredSingleAccount">
<f7-card-content class="display-flex align-items-center padding-half">
<ItemIcon icon-type="account" :icon-id="filteredSingleAccount.icon" :color="filteredSingleAccount.color" />
<div class="margin-inline-start-half">
<div class="font-weight-bold">{{ filteredSingleAccount.name }}</div>
<div><small>{{ filteredAccountBalanceText }}</small></div>
</div>
</f7-card-content>
</f7-card>
<f7-block class="transaction-calendar-container margin-vertical" v-if="pageType === TransactionListPageType.Calendar.type"> <f7-block class="transaction-calendar-container margin-vertical" v-if="pageType === TransactionListPageType.Calendar.type">
<transaction-calendar calendar-class="justify-content-center" week-day-name-type="short" <transaction-calendar calendar-class="justify-content-center" week-day-name-type="short"
:readonly="loading" :is-dark-mode="isDarkMode" :readonly="loading" :is-dark-mode="isDarkMode"
@@ -670,7 +680,8 @@ const {
tt, tt,
getCurrentLanguageTextDirection, getCurrentLanguageTextDirection,
getCurrentNumeralSystemType, getCurrentNumeralSystemType,
getWeekdayShortName getWeekdayShortName,
formatAmountToLocalizedNumeralsWithCurrency
} = useI18n(); } = useI18n();
const { showAlert, showToast, routeBackOnError } = useI18nUIComponents(); const { showAlert, showToast, routeBackOnError } = useI18nUIComponents();
@@ -748,6 +759,24 @@ const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirect
const numeralSystem = computed<NumeralSystem>(() => getCurrentNumeralSystemType()); const numeralSystem = computed<NumeralSystem>(() => getCurrentNumeralSystemType());
const isDarkMode = computed<boolean>(() => environmentsStore.framework7DarkMode || false); const isDarkMode = computed<boolean>(() => environmentsStore.framework7DarkMode || false);
const filteredSingleAccount = computed(() => {
if (queryAllFilterAccountIdsCount.value !== 1) return null;
return allAccountsMap.value[query.value.accountIds] ?? null;
});
const filteredAccountBalanceText = computed<string>(() => {
const account = filteredSingleAccount.value;
if (!account) return '';
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 transactions = computed<TransactionMonthList[]>(() => { const transactions = computed<TransactionMonthList[]>(() => {
if (loading.value) { if (loading.value) {
return []; return [];