support filter multiple accounts and categories in transaction list page

This commit is contained in:
MaysWind
2024-07-06 22:05:48 +08:00
parent c0cc9b5247
commit 3dd39defc1
13 changed files with 342 additions and 94 deletions
@@ -115,11 +115,11 @@
</v-col>
<v-col cols="12">
<account-filter-settings-card :auto-save="true" :modify-default="true" />
<account-filter-settings-card type="statisticsDefault" :auto-save="true" />
</v-col>
<v-col cols="12">
<category-filter-settings-card :auto-save="true" :modify-default="true" />
<category-filter-settings-card type="statisticsDefault" :auto-save="true" />
</v-col>
</v-row>
</template>
@@ -125,6 +125,7 @@
import { mapStores } from 'pinia';
import { useSettingsStore } from '@/stores/setting.js';
import { useAccountsStore } from '@/stores/account.js';
import { useTransactionsStore } from '@/stores/transaction.js';
import { useStatisticsStore } from '@/stores/statistics.js';
import accountConstants from '@/consts/account.js';
@@ -149,7 +150,7 @@ import {
export default {
props: [
'dialogMode',
'modifyDefault',
'type',
'autoSave'
],
emits: [
@@ -169,16 +170,16 @@ export default {
}
},
computed: {
...mapStores(useSettingsStore, useAccountsStore, useStatisticsStore),
...mapStores(useSettingsStore, useAccountsStore, useTransactionsStore, useStatisticsStore),
title() {
if (this.modifyDefault) {
if (this.type === 'statisticsDefault') {
return 'Default Account Filter';
} else {
return 'Filter Accounts';
}
},
applyText() {
if (this.modifyDefault) {
if (this.type === 'statisticsDefault') {
return 'Save';
} else {
return 'Apply';
@@ -210,13 +211,33 @@ export default {
}
const account = self.accountsStore.allAccountsMap[accountId];
allAccountIds[account.id] = false;
if (this.type === 'transactionListCurrent' && self.transactionsStore.allFilterAccountIdsCount > 0) {
allAccountIds[account.id] = true;
} else {
allAccountIds[account.id] = false;
}
}
if (self.modifyDefault) {
if (this.type === 'statisticsDefault') {
self.filterAccountIds = copyObjectTo(self.settingsStore.appSettings.statistics.defaultAccountFilter, allAccountIds);
} else {
} else if (this.type === 'statisticsCurrent') {
self.filterAccountIds = copyObjectTo(self.statisticsStore.transactionStatisticsFilter.filterAccountIds, allAccountIds);
} else if (this.type === 'transactionListCurrent') {
for (let accountId in self.transactionsStore.allFilterAccountIds) {
if (!Object.prototype.hasOwnProperty.call(self.transactionsStore.allFilterAccountIds, accountId)) {
continue;
}
const account = self.accountsStore.allAccountsMap[accountId];
if (account) {
selectAccountOrSubAccounts(allAccountIds, account, false);
}
}
self.filterAccountIds = allAccountIds;
} else {
self.$refs.snackbar.showError('Parameter Invalid');
}
}).catch(error => {
self.loading = false;
@@ -231,26 +252,40 @@ export default {
const self = this;
const filteredAccountIds = {};
let finalAccountIds = '';
for (let accountId in self.filterAccountIds) {
if (!Object.prototype.hasOwnProperty.call(self.filterAccountIds, accountId)) {
continue;
}
if (self.filterAccountIds[accountId]) {
const account = self.accountsStore.allAccountsMap[accountId];
if (!isAccountOrSubAccountsAllChecked(account, self.filterAccountIds)) {
filteredAccountIds[accountId] = true;
} else {
if (finalAccountIds.length > 0) {
finalAccountIds += ',';
}
finalAccountIds += accountId;
}
}
if (self.modifyDefault) {
if (this.type === 'statisticsDefault') {
self.settingsStore.setStatisticsDefaultAccountFilter(filteredAccountIds);
} else {
} else if (this.type === 'statisticsCurrent') {
self.statisticsStore.updateTransactionStatisticsFilter({
filterAccountIds: filteredAccountIds
});
} else if (this.type === 'transactionListCurrent') {
self.transactionsStore.updateTransactionListFilter({
accountIds: finalAccountIds
});
self.transactionsStore.updateTransactionListInvalidState(true);
}
this.$emit('settings:change', true);
self.$emit('settings:change', true);
},
cancel() {
this.$emit('settings:change', false);
@@ -123,6 +123,7 @@
import { mapStores } from 'pinia';
import { useSettingsStore } from '@/stores/setting.js';
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
import { useTransactionsStore } from '@/stores/transaction.js';
import { useStatisticsStore } from '@/stores/statistics.js';
import categoryConstants from '@/consts/category.js';
@@ -135,6 +136,7 @@ import {
selectAll,
selectNone,
selectInvert,
isCategoryOrSubCategoriesAllChecked,
isSubCategoriesAllChecked,
isSubCategoriesHasButNotAllChecked
} from '@/lib/category.js';
@@ -149,7 +151,7 @@ import {
export default {
props: [
'dialogMode',
'modifyDefault',
'type',
'autoSave'
],
emits: [
@@ -173,16 +175,16 @@ export default {
}
},
computed: {
...mapStores(useSettingsStore, useTransactionCategoriesStore, useStatisticsStore),
...mapStores(useSettingsStore, useTransactionCategoriesStore, useTransactionsStore, useStatisticsStore),
title() {
if (this.modifyDefault) {
if (this.type === 'statisticsDefault') {
return 'Default Transaction Category Filter';
} else {
return 'Filter Transaction Categories';
}
},
applyText() {
if (this.modifyDefault) {
if (this.type === 'statisticsDefault') {
return 'Save';
} else {
return 'Apply';
@@ -214,13 +216,36 @@ export default {
}
const category = self.transactionCategoriesStore.allTransactionCategoriesMap[categoryId];
allCategoryIds[category.id] = false;
if (this.type === 'transactionListCurrent' && self.transactionsStore.allFilterCategoryIdsCount > 0) {
allCategoryIds[category.id] = true;
} else {
allCategoryIds[category.id] = false;
}
}
if (self.modifyDefault) {
if (this.type === 'statisticsDefault') {
self.filterCategoryIds = copyObjectTo(self.settingsStore.appSettings.statistics.defaultTransactionCategoryFilter, allCategoryIds);
} else {
} else if (this.type === 'statisticsCurrent') {
self.filterCategoryIds = copyObjectTo(self.statisticsStore.transactionStatisticsFilter.filterCategoryIds, allCategoryIds);
} else if (this.type === 'transactionListCurrent') {
for (let categoryId in self.transactionsStore.allFilterCategoryIds) {
if (!Object.prototype.hasOwnProperty.call(self.transactionsStore.allFilterCategoryIds, categoryId)) {
continue;
}
const category = self.transactionCategoriesStore.allTransactionCategoriesMap[categoryId];
if (category && (!category.subCategories || !category.subCategories.length)) {
allCategoryIds[category.id] = false;
} else if (category) {
selectSubCategories(allCategoryIds, category, false);
}
}
self.filterCategoryIds = allCategoryIds;
} else {
self.$refs.snackbar.showError('Parameter Invalid');
}
}).catch(error => {
self.loading = false;
@@ -235,26 +260,40 @@ export default {
const self = this;
const filteredCategoryIds = {};
let finalCategoryIds = '';
for (let categoryId in self.filterCategoryIds) {
if (!Object.prototype.hasOwnProperty.call(self.filterCategoryIds, categoryId)) {
continue;
}
if (self.filterCategoryIds[categoryId]) {
const category = self.transactionCategoriesStore.allTransactionCategoriesMap[categoryId];
if (!isCategoryOrSubCategoriesAllChecked(category, self.filterCategoryIds)) {
filteredCategoryIds[categoryId] = true;
} else {
if (finalCategoryIds.length > 0) {
finalCategoryIds += ',';
}
finalCategoryIds += categoryId;
}
}
if (self.modifyDefault) {
if (this.type === 'statisticsDefault') {
self.settingsStore.setStatisticsDefaultTransactionCategoryFilter(filteredCategoryIds);
} else {
} else if (this.type === 'statisticsCurrent') {
self.statisticsStore.updateTransactionStatisticsFilter({
filterCategoryIds: filteredCategoryIds
});
} else if (this.type === 'transactionListCurrent') {
self.transactionsStore.updateTransactionListFilter({
categoryIds: finalCategoryIds
});
self.transactionsStore.updateTransactionListInvalidState(true);
}
this.$emit('settings:change', true);
self.$emit('settings:change', true);
},
cancel() {
this.$emit('settings:change', false);
@@ -276,14 +276,12 @@
@dateRange:change="setCustomDateFilter" />
<v-dialog width="800" v-model="showFilterAccountDialog">
<account-filter-settings-card
:dialog-mode="true" :modify-default="false"
<account-filter-settings-card type="statisticsCurrent" :dialog-mode="true"
@settings:change="showFilterAccountDialog = false" />
</v-dialog>
<v-dialog width="800" v-model="showFilterCategoryDialog">
<category-filter-settings-card
:dialog-mode="true" :modify-default="false"
<category-filter-settings-card type="statisticsCurrent" :dialog-mode="true"
@settings:change="showFilterCategoryDialog = false" />
</v-dialog>
+84 -23
View File
@@ -121,7 +121,7 @@
<v-icon :icon="icons.dropdownMenu" v-show="query.type !== 1" />
</div>
</template>
<v-list :selected="[query.categoryIds]">
<v-list :selected="queryAllFilterCategoryIdsArray">
<v-list-item key="" value="" class="text-sm" density="compact"
:class="{ 'list-item-selected': !query.categoryIds }"
:append-icon="(!query.categoryIds ? icons.check : null)">
@@ -137,7 +137,7 @@
:class="{ 'list-item-selected': query.categoryIds && queryAllFilterCategoryIdsCount > 1 }"
:append-icon="(query.categoryIds && queryAllFilterCategoryIdsCount > 1 ? icons.check : null)">
<v-list-item-title class="cursor-pointer"
@click="showMultipleCategoriesDialog()">
@click="showFilterCategoryDialog = true">
<div class="d-flex align-center">
<v-icon :icon="icons.multiple" />
<span class="text-sm ml-3">{{ $t('Multiple Categories') }}</span>
@@ -170,8 +170,9 @@
<v-divider />
<v-list-item class="text-sm" density="compact"
:class="{ 'item-in-multiple-selection': queryAllFilterCategoryIdsCount > 1 && queryAllFilterCategoryIds[category.id] }"
:value="category.id"
:append-icon="(queryAllFilterCategoryIds[category.id] ? icons.check : null)">
:append-icon="(query.categoryIds === category.id ? icons.check : null)">
<v-list-item-title class="cursor-pointer"
@click="changeCategoryFilter(category.id)">
<div class="d-flex align-center">
@@ -186,8 +187,8 @@
<v-divider v-if="!subCategory.hidden" />
<v-list-item class="text-sm" density="compact"
:value="subCategory.id"
:class="{ 'list-item-selected': queryAllFilterCategoryIds[subCategory.id] }"
:append-icon="(queryAllFilterCategoryIds[subCategory.id] ? icons.check : null)"
:class="{ 'list-item-selected': query.categoryIds === subCategory.id, 'item-in-multiple-selection': queryAllFilterCategoryIdsCount > 1 && queryAllFilterCategoryIds[subCategory.id] }"
:append-icon="(query.categoryIds === subCategory.id ? icons.check : null)"
v-if="!subCategory.hidden">
<v-list-item-title class="cursor-pointer"
@click="changeCategoryFilter(subCategory.id)">
@@ -264,7 +265,7 @@
<v-icon :icon="icons.dropdownMenu" />
</div>
</template>
<v-list :selected="[query.accountIds]">
<v-list :selected="queryAllFilterAccountIdsArray">
<v-list-item key="" value="" class="text-sm" density="compact"
:class="{ 'list-item-selected': !query.accountIds }"
:append-icon="(!query.accountIds ? icons.check : null)">
@@ -280,7 +281,7 @@
:class="{ 'list-item-selected': query.accountIds && queryAllFilterAccountIdsCount > 1 }"
:append-icon="(query.accountIds && queryAllFilterAccountIdsCount > 1 ? icons.check : null)">
<v-list-item-title class="cursor-pointer"
@click="showMultipleAccountsDialog()">
@click="showFilterAccountDialog = true">
<div class="d-flex align-center">
<v-icon :icon="icons.multiple" />
<span class="text-sm ml-3">{{ $t('Multiple Accounts') }}</span>
@@ -292,8 +293,8 @@
<v-divider v-if="!account.hidden" />
<v-list-item class="text-sm" density="compact"
:value="account.id"
:class="{ 'list-item-selected': queryAllFilterAccountIds[account.id] }"
:append-icon="(queryAllFilterAccountIds[account.id] ? icons.check : null)"
:class="{ 'list-item-selected': query.accountIds === account.id, 'item-in-multiple-selection': queryAllFilterAccountIdsCount > 1 && queryAllFilterAccountIds[account.id] }"
:append-icon="(query.accountIds === account.id ? icons.check : null)"
v-if="!account.hidden">
<v-list-item-title class="cursor-pointer"
@click="changeAccountFilter(account.id)">
@@ -406,12 +407,24 @@
@dateRange:change="changeCustomDateFilter" />
<edit-dialog ref="editDialog" :persistent="true" />
<v-dialog width="800" v-model="showFilterAccountDialog">
<account-filter-settings-card type="transactionListCurrent" :dialog-mode="true"
@settings:change="changeMultipleAccountsFilter" />
</v-dialog>
<v-dialog width="800" v-model="showFilterCategoryDialog">
<category-filter-settings-card type="transactionListCurrent" :dialog-mode="true"
@settings:change="changeMultipleCategoriesFilter" />
</v-dialog>
<confirm-dialog ref="confirmDialog"/>
<snack-bar ref="snackbar" />
</template>
<script>
import EditDialog from './list/dialogs/EditDialog.vue';
import AccountFilterSettingsCard from '@/views/desktop/common/cards/AccountFilterSettingsCard.vue';
import CategoryFilterSettingsCard from '@/views/desktop/common/cards/CategoryFilterSettingsCard.vue';
import { useDisplay } from 'vuetify';
@@ -426,7 +439,7 @@ import numeralConstants from '@/consts/numeral.js';
import datetimeConstants from '@/consts/datetime.js';
import accountConstants from '@/consts/account.js';
import transactionConstants from '@/consts/transaction.js';
import { isString, getNameByKeyValue } from '@/lib/common.js';
import { isString, getNameByKeyValue, objectToArray } from '@/lib/common.js';
import logger from '@/lib/logger.js';
import {
getCurrentUnixTime,
@@ -470,7 +483,9 @@ import {
export default {
components: {
EditDialog
EditDialog,
AccountFilterSettingsCard,
CategoryFilterSettingsCard
},
props: [
'initDateType',
@@ -504,6 +519,8 @@ export default {
alwaysShowNav: mdAndUp.value,
showNav: mdAndUp.value,
showCustomDateRangeDialog: false,
showFilterAccountDialog: false,
showFilterCategoryDialog: false,
icons: {
search: mdiMagnify,
check: mdiCheck,
@@ -574,6 +591,24 @@ export default {
queryAllFilterAccountIdsCount() {
return this.transactionsStore.allFilterAccountIdsCount;
},
queryAllFilterCategoryIdsArray() {
if (this.queryAllFilterCategoryIdsCount === 0) {
return [''];
} else if (this.queryAllFilterCategoryIdsCount === 1) {
return [this.query.categoryIds];
} else { // this.queryAllFilterCategoryIdsCount > 1
return ['multiple'];
}
},
queryAllFilterAccountIdsArray() {
if (this.queryAllFilterAccountIdsCount === 0) {
return [''];
} else if (this.queryAllFilterAccountIdsCount === 1) {
return [this.query.accountIds];
} else { // this.queryAllFilterAccountIdsCount > 1
return ['multiple'];
}
},
queryCategoryName() {
if (this.queryAllFilterCategoryIdsCount > 1) {
return this.$t('Multiple Categories');
@@ -945,9 +980,11 @@ export default {
this.$router.push(this.getFilterLinkUrl());
},
changeTypeFilter(type) {
let removeCategoryFilter = false;
let newCategoryFilter = undefined;
if (type && this.query.categoryIds) {
newCategoryFilter = '';
for (let categoryId in this.queryAllFilterCategoryIds) {
if (!Object.prototype.hasOwnProperty.call(this.queryAllFilterCategoryIds, categoryId)) {
continue;
@@ -955,16 +992,19 @@ export default {
const category = this.allCategories[categoryId];
if (category && category.type !== transactionTypeToCategoryType(type)) {
removeCategoryFilter = true;
break;
if (category && category.type === transactionTypeToCategoryType(type)) {
if (newCategoryFilter.length > 0) {
newCategoryFilter += ',';
}
newCategoryFilter += categoryId;
}
}
}
this.transactionsStore.updateTransactionListFilter({
type: type,
categoryIds: removeCategoryFilter ? '' : undefined
categoryIds: newCategoryFilter
});
this.loading = true;
@@ -988,6 +1028,19 @@ export default {
this.transactionsStore.clearTransactions();
this.$router.push(this.getFilterLinkUrl());
},
changeMultipleCategoriesFilter(changed) {
this.categoryMenuState = false;
this.showFilterCategoryDialog = false;
if (!changed) {
return;
}
this.loading = true;
this.currentPageTransactions = [];
this.transactionsStore.clearTransactions();
this.$router.push(this.getFilterLinkUrl());
},
changeAmountFilter(filterType) {
this.currentAmountFilterType = '';
this.amountMenuState = false;
@@ -1046,6 +1099,18 @@ export default {
this.transactionsStore.clearTransactions();
this.$router.push(this.getFilterLinkUrl());
},
changeMultipleAccountsFilter(changed) {
this.showFilterAccountDialog = false;
if (!changed) {
return;
}
this.loading = true;
this.currentPageTransactions = [];
this.transactionsStore.clearTransactions();
this.$router.push(this.getFilterLinkUrl());
},
changeKeywordFilter(keyword) {
if (this.query.keyword === keyword) {
return;
@@ -1059,12 +1124,6 @@ export default {
this.currentPageTransactions = [];
this.transactionsStore.clearTransactions();
this.$router.push(this.getFilterLinkUrl());
},
showMultipleCategoriesDialog() {
},
showMultipleAccountsDialog() {
},
add() {
const self = this;
@@ -1297,7 +1356,9 @@ export default {
padding: 0 8px 0 8px;
}
.transaction-category-menu .has-children-item-selected span {
.transaction-category-menu .has-children-item-selected span,
.transaction-category-menu .item-in-multiple-selection span,
.transaction-account-menu .item-in-multiple-selection span {
font-weight: bold;
}
</style>