From e825323bb03107789e6ff0c0fce55061fe632a9e Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sun, 14 Dec 2025 17:41:50 +0800 Subject: [PATCH] add search box in filter account page / dialog --- src/lib/account.ts | 104 +++++------- src/locales/de.json | 1 - src/locales/en.json | 1 - src/locales/es.json | 1 - src/locales/fr.json | 1 - src/locales/it.json | 1 - src/locales/ja.json | 1 - src/locales/kn.json | 1 - src/locales/ko.json | 1 - src/locales/nl.json | 1 - src/locales/pt_BR.json | 1 - src/locales/ru.json | 1 - src/locales/th.json | 1 - src/locales/tr.json | 1 - src/locales/uk.json | 1 - src/locales/vi.json | 1 - src/locales/zh_Hans.json | 1 - src/locales/zh_Hant.json | 1 - src/models/account.ts | 33 ++-- .../settings/AccountFilterSettingPageBase.ts | 39 ++++- .../cards/AccountFilterSettingsCard.vue | 154 +++++++----------- .../settings/AccountFilterSettingsPage.vue | 51 +++--- 22 files changed, 180 insertions(+), 218 deletions(-) diff --git a/src/lib/account.ts b/src/lib/account.ts index 8d65e178..43f7cf7e 100644 --- a/src/lib/account.ts +++ b/src/lib/account.ts @@ -1,7 +1,7 @@ -import { itemAndIndex, keys, keysIfValueEquals, values } from '@/core/base.ts'; +import { keys, keysIfValueEquals, values } from '@/core/base.ts'; import { AccountType, AccountCategory } from '@/core/account.ts'; import { PARENT_ACCOUNT_CURRENCY_PLACEHOLDER } from '@/consts/currency.ts'; -import { type AccountBalance, type CategorizedAccount, type AccountCategoriesWithVisibleCount, Account } from '@/models/account.ts'; +import { type AccountBalance, type CategorizedAccount, Account } from '@/models/account.ts'; export function getCategorizedAccountsMap(allAccounts: Account[]): Record { const ret: Record = {}; @@ -71,67 +71,63 @@ export function getAccountMapByName(allAccounts: Account[]): Record): AccountCategoriesWithVisibleCount[] { - const ret: AccountCategoriesWithVisibleCount[] = []; +export function filterCategorizedAccounts(categorizedAccountsMap: Record, allowAccountName?: string, showHidden?: boolean): Record { + const ret: Record = {}; const allCategories = AccountCategory.values(); + const lowercaseFilterContent = allowAccountName ? allowAccountName.toLowerCase() : ''; for (const accountCategory of allCategories) { const categorizedAccount = categorizedAccountsMap[accountCategory.type]; - if (!categorizedAccount || !categorizedAccount.accounts) { + if (!categorizedAccount || !categorizedAccount.accounts || categorizedAccount.accounts.length < 1) { continue; } - const allAccounts = categorizedAccount.accounts; - const allSubAccounts: Record = {}; - const allVisibleSubAccountCounts: Record = {}; - const allFirstVisibleSubAccountIndexes: Record = {}; - let allVisibleAccountCount = 0; - let firstVisibleAccountIndex = -1; + const allFilteredAccounts: Account[] = []; - for (const [account, accountIndex] of itemAndIndex(allAccounts)) { - if (!account.hidden) { - allVisibleAccountCount++; - - if (firstVisibleAccountIndex === -1) { - firstVisibleAccountIndex = accountIndex; - } + for (const account of categorizedAccount.accounts) { + if (!showHidden && account.hidden) { + continue; } - if (account.type === AccountType.MultiSubAccounts.type && account.subAccounts) { - let visibleSubAccountCount = 0; - let firstVisibleSubAccountIndex = -1; + const accountMatchesName = !lowercaseFilterContent || account.name.toLowerCase().includes(lowercaseFilterContent); + const filteredSubAccounts: Account[] = []; - for (const [subAccount, subAccountIndex] of itemAndIndex(account.subAccounts)) { - if (!subAccount.hidden) { - visibleSubAccountCount++; - - if (firstVisibleSubAccountIndex === -1) { - firstVisibleSubAccountIndex = subAccountIndex; - } + if (account.subAccounts) { + for (const subAccount of account.subAccounts) { + if (!showHidden && subAccount.hidden) { + continue; } - } - if (account.subAccounts.length > 0) { - allSubAccounts[account.id] = account.subAccounts; - allVisibleSubAccountCounts[account.id] = visibleSubAccountCount; - allFirstVisibleSubAccountIndexes[account.id] = firstVisibleSubAccountIndex; + if (!accountMatchesName && lowercaseFilterContent && !subAccount.name.toLowerCase().includes(lowercaseFilterContent)) { + continue; + } + + const filteredSubAccount = subAccount.clone(); + filteredSubAccounts.push(filteredSubAccount); } } + + if (!accountMatchesName && filteredSubAccounts.length < 1) { + continue; + } + + const filteredAccount = account.cloneSelf(); + + if (filteredAccount.type === AccountType.MultiSubAccounts.type) { + filteredAccount.subAccounts = filteredSubAccounts; + } + + allFilteredAccounts.push(filteredAccount); } - if (allAccounts.length > 0) { - ret.push({ - category: accountCategory.type, - name: accountCategory.name, - icon: accountCategory.defaultAccountIconId, - allAccounts: allAccounts, - allVisibleAccountCount: allVisibleAccountCount, - firstVisibleAccountIndex: firstVisibleAccountIndex, - allSubAccounts: allSubAccounts, - allVisibleSubAccountCounts: allVisibleSubAccountCounts, - allFirstVisibleSubAccountIndexes: allFirstVisibleSubAccountIndexes - }); + if (allFilteredAccounts.length > 0) { + ret[accountCategory.type] = { + category: categorizedAccount.category, + name: categorizedAccount.name, + icon: categorizedAccount.icon, + accounts: allFilteredAccounts + }; } } @@ -269,42 +265,30 @@ export function selectAllVisible(filterAccountIds: Record, allA } } -export function selectAll(filterAccountIds: Record, allAccountsMap: Record, skipHiddenAccount: boolean): void { +export function selectAll(filterAccountIds: Record, allAccountsMap: Record): void { for (const accountId of keys(filterAccountIds)) { const account = allAccountsMap[accountId]; - if (skipHiddenAccount && account && account.hidden) { - continue; - } - if (account && account.type === AccountType.SingleAccount.type) { filterAccountIds[account.id] = false; } } } -export function selectNone(filterAccountIds: Record, allAccountsMap: Record, skipHiddenAccount: boolean): void { +export function selectNone(filterAccountIds: Record, allAccountsMap: Record): void { for (const accountId of keys(filterAccountIds)) { const account = allAccountsMap[accountId]; - if (skipHiddenAccount && account && account.hidden) { - continue; - } - if (account && account.type === AccountType.SingleAccount.type) { filterAccountIds[account.id] = true; } } } -export function selectInvert(filterAccountIds: Record, allAccountsMap: Record, skipHiddenAccount: boolean): void { +export function selectInvert(filterAccountIds: Record, allAccountsMap: Record): void { for (const accountId of keys(filterAccountIds)) { const account = allAccountsMap[accountId]; - if (skipHiddenAccount && account && account.hidden) { - continue; - } - if (account && account.type === AccountType.SingleAccount.type) { filterAccountIds[account.id] = !filterAccountIds[account.id]; } diff --git a/src/locales/de.json b/src/locales/de.json index 4422e2b1..777f9d0e 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -1551,7 +1551,6 @@ "Select All": "Alle auswählen", "Select None": "Keine auswählen", "Invert Selection": "Auswahl umkehren", - "Select All Visible": "Select All Visible", "Select All in This Page": "Alle auf dieser Seite auswählen", "Select None in This Page": "Keine auf dieser Seite auswählen", "Invert Selection in This Page": "Auswahl auf dieser Seite umkehren", diff --git a/src/locales/en.json b/src/locales/en.json index ff5cbaf7..9e7f8cc0 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1551,7 +1551,6 @@ "Select All": "Select All", "Select None": "Select None", "Invert Selection": "Invert Selection", - "Select All Visible": "Select All Visible", "Select All in This Page": "Select All in This Page", "Select None in This Page": "Select None in This Page", "Invert Selection in This Page": "Invert Selection in This Page", diff --git a/src/locales/es.json b/src/locales/es.json index 4e8e01d4..ebe55baf 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -1551,7 +1551,6 @@ "Select All": "Seleccionar Todo", "Select None": "Seleccionar Nada", "Invert Selection": "Invertir Selección", - "Select All Visible": "Seleccionar Visibles", "Select All in This Page": "Seleccionar Todo en Esta Página", "Select None in This Page": "No Seleccionar Ninguno en Esta Página", "Invert Selection in This Page": "Invertir Selección en Esta Página", diff --git a/src/locales/fr.json b/src/locales/fr.json index d114dbf9..f0905d74 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -1551,7 +1551,6 @@ "Select All": "Tout sélectionner", "Select None": "Ne rien sélectionner", "Invert Selection": "Inverser la sélection", - "Select All Visible": "Select All Visible", "Select All in This Page": "Tout sélectionner dans cette page", "Select None in This Page": "Ne rien sélectionner dans cette page", "Invert Selection in This Page": "Inverser la sélection dans cette page", diff --git a/src/locales/it.json b/src/locales/it.json index 7a025033..5826e2e5 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -1551,7 +1551,6 @@ "Select All": "Seleziona tutto", "Select None": "Deseleziona tutto", "Invert Selection": "Inverti selezione", - "Select All Visible": "Select All Visible", "Select All in This Page": "Seleziona tutto in questa pagina", "Select None in This Page": "Deseleziona tutto in questa pagina", "Invert Selection in This Page": "Inverti selezione in questa pagina", diff --git a/src/locales/ja.json b/src/locales/ja.json index 36f0799c..d595f2d1 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -1551,7 +1551,6 @@ "Select All": "すべて選択", "Select None": "選択解除", "Invert Selection": "選択を反転", - "Select All Visible": "Select All Visible", "Select All in This Page": "このページをすべて選択", "Select None in This Page": "このページの選択を解除", "Invert Selection in This Page": "このページの選択を反転", diff --git a/src/locales/kn.json b/src/locales/kn.json index 0a329060..e6653113 100644 --- a/src/locales/kn.json +++ b/src/locales/kn.json @@ -1551,7 +1551,6 @@ "Select All": "ಎಲ್ಲಾ ಆಯ್ಕೆಮಾಡಿ", "Select None": "ಯಾವುದೂ ಆಯ್ಕೆ ಮಾಡಬೇಡಿ", "Invert Selection": "ಆಯ್ಕೆಯನ್ನು ತಿರುಗಿಸಿ", - "Select All Visible": "ಗೋಚರಿಸುವ ಎಲ್ಲವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ", "Select All in This Page": "ಈ ಪುಟದ ಎಲ್ಲವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ", "Select None in This Page": "ಈ ಪುಟದಲ್ಲಿ ಯಾವುದನ್ನೂ ಆಯ್ಕೆ ಮಾಡಬೇಡಿ", "Invert Selection in This Page": "ಈ ಪುಟದಲ್ಲಿ ಆಯ್ಕೆಯನ್ನು ತಿರುಗಿಸಿ", diff --git a/src/locales/ko.json b/src/locales/ko.json index f5098ff4..3e3b4031 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -1551,7 +1551,6 @@ "Select All": "전체 선택", "Select None": "선택 해제", "Invert Selection": "선택 반전", - "Select All Visible": "Select All Visible", "Select All in This Page": "현재 페이지 전체 선택", "Select None in This Page": "현재 페이지 선택 해제", "Invert Selection in This Page": "현재 페이지 선택 반전", diff --git a/src/locales/nl.json b/src/locales/nl.json index a47f4496..5fd5cc38 100644 --- a/src/locales/nl.json +++ b/src/locales/nl.json @@ -1551,7 +1551,6 @@ "Select All": "Alles selecteren", "Select None": "Niets selecteren", "Invert Selection": "Selectie omkeren", - "Select All Visible": "Select All Visible", "Select All in This Page": "Alles op deze pagina selecteren", "Select None in This Page": "Niets op deze pagina selecteren", "Invert Selection in This Page": "Selectie op deze pagina omkeren", diff --git a/src/locales/pt_BR.json b/src/locales/pt_BR.json index 6100461f..f883a70b 100644 --- a/src/locales/pt_BR.json +++ b/src/locales/pt_BR.json @@ -1551,7 +1551,6 @@ "Select All": "Selecionar Todos", "Select None": "Selecionar Nenhum", "Invert Selection": "Inverter Seleção", - "Select All Visible": "Select All Visible", "Select All in This Page": "Selecionar Todos nesta Página", "Select None in This Page": "Selecionar Nenhum nesta Página", "Invert Selection in This Page": "Inverter Seleção nesta Página", diff --git a/src/locales/ru.json b/src/locales/ru.json index 2cc34b7e..15a8b9b8 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -1551,7 +1551,6 @@ "Select All": "Выбрать все", "Select None": "Отменить выбор", "Invert Selection": "Инвертировать выбор", - "Select All Visible": "Select All Visible", "Select All in This Page": "Выбрать все на этой странице", "Select None in This Page": "Отменить выбор на этой странице", "Invert Selection in This Page": "Инвертировать выбор на этой странице", diff --git a/src/locales/th.json b/src/locales/th.json index 14b2c2d0..31041407 100644 --- a/src/locales/th.json +++ b/src/locales/th.json @@ -1551,7 +1551,6 @@ "Select All": "เลือกทั้งหมด", "Select None": "ยกเลิกการเลือกทั้งหมด", "Invert Selection": "สลับการเลือก", - "Select All Visible": "Select All Visible", "Select All in This Page": "เลือกทั้งหมดในหน้านี้", "Select None in This Page": "ยกเลิกการเลือกทั้งหมดในหน้านี้", "Invert Selection in This Page": "สลับการเลือกในหน้านี้", diff --git a/src/locales/tr.json b/src/locales/tr.json index 67ff2365..1fe7425b 100644 --- a/src/locales/tr.json +++ b/src/locales/tr.json @@ -1551,7 +1551,6 @@ "Select All": "Tümünü Seç", "Select None": "Hiçbirini Seçme", "Invert Selection": "Seçimi Tersine Çevir", - "Select All Visible": "Görünenlerin Tümünü Seç", "Select All in This Page": "Bu Sayfadakilerin Tümünü Seç", "Select None in This Page": "Bu Sayfadakilerin Hiçbirini Seçme", "Invert Selection in This Page": "Bu Sayfadaki Seçimi Tersine Çevir", diff --git a/src/locales/uk.json b/src/locales/uk.json index 018d00d6..81c22ba4 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -1551,7 +1551,6 @@ "Select All": "Вибрати все", "Select None": "Скасувати вибір", "Invert Selection": "Інвертувати вибір", - "Select All Visible": "Select All Visible", "Select All in This Page": "Вибрати все на цій сторінці", "Select None in This Page": "Скасувати вибір на цій сторінці", "Invert Selection in This Page": "Інвертувати вибір на цій сторінці", diff --git a/src/locales/vi.json b/src/locales/vi.json index 332cdb60..1863ec32 100644 --- a/src/locales/vi.json +++ b/src/locales/vi.json @@ -1551,7 +1551,6 @@ "Select All": "Chọn tất cả", "Select None": "Bỏ chọn tất cả", "Invert Selection": "Đảo ngược lựa chọn", - "Select All Visible": "Select All Visible", "Select All in This Page": "Chọn tất cả trong trang này", "Select None in This Page": "Bỏ chọn tất cả trong trang này", "Invert Selection in This Page": "Đảo ngược lựa chọn trong trang này", diff --git a/src/locales/zh_Hans.json b/src/locales/zh_Hans.json index 6b767ff0..fae2077d 100644 --- a/src/locales/zh_Hans.json +++ b/src/locales/zh_Hans.json @@ -1551,7 +1551,6 @@ "Select All": "全部选择", "Select None": "全部不选", "Invert Selection": "反向选择", - "Select All Visible": "全部选择可见", "Select All in This Page": "本页全选", "Select None in This Page": "本页不选", "Invert Selection in This Page": "本页反选", diff --git a/src/locales/zh_Hant.json b/src/locales/zh_Hant.json index 31c31c48..1114cd33 100644 --- a/src/locales/zh_Hant.json +++ b/src/locales/zh_Hant.json @@ -1551,7 +1551,6 @@ "Select All": "全選", "Select None": "取消全選", "Invert Selection": "反向選擇", - "Select All Visible": "全部選擇可見", "Select All in This Page": "本頁全選", "Select None in This Page": "取消本頁全選", "Invert Selection in This Page": "本頁反向選擇", diff --git a/src/models/account.ts b/src/models/account.ts index a5427104..dec7ba4f 100644 --- a/src/models/account.ts +++ b/src/models/account.ts @@ -349,6 +349,27 @@ export class Account implements AccountInfoResponse { return subAccountCurrencies; } + public cloneSelf(): Account { + return new Account( + this.id, + this.name, + this.parentId, + this.category, + this.type, + this.icon, + this.color, + this.currency, + this.balance, + this.comment, + this.displayOrder, + this.visible, + this.balanceTime, + this.creditCardStatementDate, + this.isAsset, + this.isLiability + ); + } + public clone(): Account { return new Account( this.id, @@ -658,18 +679,6 @@ export class CategorizedAccountWithDisplayBalance { } } -export interface AccountCategoriesWithVisibleCount { - readonly category: number; - readonly name: string; - readonly icon: string; - readonly allAccounts: Account[]; - readonly allVisibleAccountCount: number; - readonly firstVisibleAccountIndex: number; - readonly allSubAccounts: Record; - readonly allVisibleSubAccountCounts: Record; - readonly allFirstVisibleSubAccountIndexes: Record; -} - export interface AccountShowingIds { readonly accounts: Record; readonly subAccounts: Record; diff --git a/src/views/base/settings/AccountFilterSettingPageBase.ts b/src/views/base/settings/AccountFilterSettingPageBase.ts index 48eb0669..95526daf 100644 --- a/src/views/base/settings/AccountFilterSettingPageBase.ts +++ b/src/views/base/settings/AccountFilterSettingPageBase.ts @@ -7,10 +7,10 @@ import { useStatisticsStore } from '@/stores/statistics.ts'; import { useOverviewStore } from '@/stores/overview.ts'; import { keys, keysIfValueEquals, values } from '@/core/base.ts'; -import type { Account, AccountCategoriesWithVisibleCount } from '@/models/account.ts'; +import type {Account, CategorizedAccount} from '@/models/account.ts'; import { - getCategorizedAccountsWithVisibleCount, + filterCategorizedAccounts, selectAccountOrSubAccounts, isAccountOrSubAccountsAllChecked } from '@/lib/account.ts'; @@ -26,6 +26,7 @@ export function useAccountFilterSettingPageBase(type?: AccountFilterType) { const loading = ref(true); const showHidden = ref(false); + const filterContent = ref(''); const filterAccountIds = ref>({}); const title = computed(() => { @@ -48,15 +49,33 @@ export function useAccountFilterSettingPageBase(type?: AccountFilterType) { return type === 'statisticsDefault' || type === 'statisticsCurrent' || type === 'homePageOverview' || type === 'transactionListCurrent'; }); - const allCategorizedAccounts = computed(() => getCategorizedAccountsWithVisibleCount(accountsStore.allCategorizedAccountsMap)); - const hasAnyAvailableAccount = computed(() => accountsStore.allAvailableAccountsCount > 0); + const allCategorizedAccounts = computed>(() => filterCategorizedAccounts(accountsStore.allCategorizedAccountsMap, filterContent.value, showHidden.value)); + const allVisibleAccountMap = computed>(() => { + const accountMap: Record = {}; - const hasAnyVisibleAccount = computed(() => { - if (showHidden.value) { - return accountsStore.allAvailableAccountsCount > 0; - } else { - return accountsStore.allVisibleAccountsCount > 0; + for (const accountCategory of values(allCategorizedAccounts.value)) { + for (const account of accountCategory.accounts) { + accountMap[account.id] = account; + + if (account.subAccounts) { + for (const subAccount of account.subAccounts) { + accountMap[subAccount.id] = subAccount; + } + } + } } + + return accountMap; + }); + const hasAnyAvailableAccount = computed(() => accountsStore.allAvailableAccountsCount > 0); + const hasAnyVisibleAccount = computed(() => { + for (const accountCategory of values(allCategorizedAccounts.value)) { + if (accountCategory.accounts.length > 0) { + return true; + } + } + + return false; }); function isAccountChecked(account: Account, filterAccountIds: Record): boolean { @@ -162,12 +181,14 @@ export function useAccountFilterSettingPageBase(type?: AccountFilterType) { // states loading, showHidden, + filterContent, filterAccountIds, // computed states title, applyText, allowHiddenAccount, allCategorizedAccounts, + allVisibleAccountMap, hasAnyAvailableAccount, hasAnyVisibleAccount, // functions diff --git a/src/views/desktop/common/cards/AccountFilterSettingsCard.vue b/src/views/desktop/common/cards/AccountFilterSettingsCard.vue index 81d29422..308d6260 100644 --- a/src/views/desktop/common/cards/AccountFilterSettingsCard.vue +++ b/src/views/desktop/common/cards/AccountFilterSettingsCard.vue @@ -1,81 +1,48 @@