support filter multiple accounts and categories in transaction list page
This commit is contained in:
@@ -262,6 +262,21 @@ export function selectInvert(filterCategoryIds, allTransactionCategoriesMap) {
|
||||
}
|
||||
}
|
||||
|
||||
export function isCategoryOrSubCategoriesAllChecked(category, filterCategoryIds) {
|
||||
if (!category.subCategories) {
|
||||
return !filterCategoryIds[category.id];
|
||||
}
|
||||
|
||||
for (let i = 0; i < category.subCategories.length; i++) {
|
||||
const subCategory = category.subCategories[i];
|
||||
if (filterCategoryIds[subCategory.id]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isSubCategoriesAllChecked(category, filterCategoryIds) {
|
||||
for (let i = 0; i < category.subCategories.length; i++) {
|
||||
const subCategory = category.subCategories[i];
|
||||
|
||||
@@ -359,6 +359,20 @@ export function arrayContainsFieldValue(array, fieldName, value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function objectToArray(object) {
|
||||
const ret = [];
|
||||
|
||||
for (let field in object) {
|
||||
if (!Object.prototype.hasOwnProperty.call(object, field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.push(field);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function categorizedArrayToPlainArray(object) {
|
||||
const ret = [];
|
||||
|
||||
|
||||
@@ -270,6 +270,10 @@ i.icon.la, i.icon.las, i.icon.lab {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.list > ul > li.item-in-multiple-selection > .item-content > .item-inner > .item-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.list.list-dividers li.has-child-list-item .item-inner:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -117,6 +117,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';
|
||||
@@ -142,23 +143,23 @@ export default {
|
||||
return {
|
||||
loading: true,
|
||||
loadingError: null,
|
||||
modifyDefault: false,
|
||||
type: null,
|
||||
filterAccountIds: {},
|
||||
collapseStates: self.getCollapseStates(),
|
||||
showMoreActionSheet: false
|
||||
}
|
||||
},
|
||||
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';
|
||||
@@ -178,7 +179,7 @@ export default {
|
||||
const self = this;
|
||||
const query = self.f7route.query;
|
||||
|
||||
self.modifyDefault = !!query.modifyDefault;
|
||||
self.type = query.type;
|
||||
|
||||
self.accountsStore.loadAllAccounts({
|
||||
force: false
|
||||
@@ -193,13 +194,34 @@ 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.$toast('Parameter Invalid');
|
||||
self.loadingError = 'Parameter Invalid';
|
||||
}
|
||||
}).catch(error => {
|
||||
if (error.processed) {
|
||||
@@ -219,23 +241,37 @@ export default {
|
||||
const router = self.f7router;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
router.back();
|
||||
|
||||
@@ -126,6 +126,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';
|
||||
@@ -138,6 +139,7 @@ import {
|
||||
selectAll,
|
||||
selectNone,
|
||||
selectInvert,
|
||||
isCategoryOrSubCategoriesAllChecked,
|
||||
isSubCategoriesAllChecked,
|
||||
isSubCategoriesHasButNotAllChecked
|
||||
} from '@/lib/category.js';
|
||||
@@ -153,23 +155,23 @@ export default {
|
||||
return {
|
||||
loading: true,
|
||||
loadingError: null,
|
||||
modifyDefault: false,
|
||||
type: null,
|
||||
filterCategoryIds: {},
|
||||
collapseStates: self.getCollapseStates(),
|
||||
showMoreActionSheet: false
|
||||
}
|
||||
},
|
||||
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';
|
||||
@@ -189,7 +191,7 @@ export default {
|
||||
const self = this;
|
||||
const query = self.f7route.query;
|
||||
|
||||
self.modifyDefault = !!query.modifyDefault;
|
||||
self.type = query.type;
|
||||
|
||||
self.transactionCategoriesStore.loadAllCategories({
|
||||
force: false
|
||||
@@ -204,13 +206,37 @@ 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.$toast('Parameter Invalid');
|
||||
self.loadingError = 'Parameter Invalid';
|
||||
}
|
||||
}).catch(error => {
|
||||
if (error.processed) {
|
||||
@@ -230,23 +256,37 @@ export default {
|
||||
const router = self.f7router;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
router.back();
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
</select>
|
||||
</f7-list-item>
|
||||
|
||||
<f7-list-item :title="$t('Default Account Filter')" link="/settings/filter/account?modifyDefault=1"></f7-list-item>
|
||||
<f7-list-item :title="$t('Default Account Filter')" link="/settings/filter/account?type=statisticsDefault"></f7-list-item>
|
||||
|
||||
<f7-list-item :title="$t('Default Transaction Category Filter')" link="/settings/filter/category?modifyDefault=1"></f7-list-item>
|
||||
<f7-list-item :title="$t('Default Transaction Category Filter')" link="/settings/filter/category?type=statisticsDefault"></f7-list-item>
|
||||
|
||||
<f7-list-item
|
||||
:title="$t('Default Sort Order')"
|
||||
|
||||
@@ -534,10 +534,10 @@ export default {
|
||||
this.f7router.navigate(this.getItemLinkUrl(item));
|
||||
},
|
||||
filterAccounts() {
|
||||
this.f7router.navigate('/settings/filter/account');
|
||||
this.f7router.navigate('/settings/filter/account?type=statisticsCurrent');
|
||||
},
|
||||
filterCategories() {
|
||||
this.f7router.navigate('/settings/filter/category');
|
||||
this.f7router.navigate('/settings/filter/category?type=statisticsCurrent');
|
||||
},
|
||||
settings() {
|
||||
this.f7router.navigate('/statistic/settings');
|
||||
|
||||
@@ -290,7 +290,7 @@
|
||||
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="!query.categoryIds"></f7-icon>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
<f7-list-item :class="{ 'list-item-selected': query.categoryIds && queryAllFilterCategoryIdsCount > 1 }" :title="$t('Multiple Categories')" @click="showMultipleCategoriesPopup()">
|
||||
<f7-list-item :class="{ 'list-item-selected': query.categoryIds && queryAllFilterCategoryIdsCount > 1 }" :title="$t('Multiple Categories')" @click="filterMultipleCategories()">
|
||||
<template #media>
|
||||
<f7-icon f7="rectangle_on_rectangle"></f7-icon>
|
||||
</template>
|
||||
@@ -317,16 +317,17 @@
|
||||
</template>
|
||||
<f7-accordion-content>
|
||||
<f7-list dividers class="padding-left">
|
||||
<f7-list-item :class="{ 'list-item-selected': queryAllFilterCategoryIds[category.id] }" :title="$t('All')" @click="changeCategoryFilter(category.id)">
|
||||
<f7-list-item :class="{ 'list-item-selected': query.categoryIds === category.id, 'item-in-multiple-selection': queryAllFilterCategoryIdsCount > 1 && queryAllFilterCategoryIds[category.id] }"
|
||||
:title="$t('All')" @click="changeCategoryFilter(category.id)">
|
||||
<template #media>
|
||||
<f7-icon f7="rectangle_grid_2x2"></f7-icon>
|
||||
</template>
|
||||
<template #after>
|
||||
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="queryAllFilterCategoryIds[category.id]"></f7-icon>
|
||||
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="query.categoryIds === category.id"></f7-icon>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
<f7-list-item :title="subCategory.name"
|
||||
:class="{ 'list-item-selected': queryAllFilterCategoryIds[subCategory.id] }"
|
||||
:class="{ 'list-item-selected': query.categoryIds === subCategory.id, 'item-in-multiple-selection': queryAllFilterCategoryIdsCount > 1 && queryAllFilterCategoryIds[subCategory.id] }"
|
||||
:key="subCategory.id"
|
||||
v-for="subCategory in category.subCategories"
|
||||
v-show="!subCategory.hidden"
|
||||
@@ -338,7 +339,7 @@
|
||||
<template #after>
|
||||
<f7-icon class="list-item-checked-icon"
|
||||
f7="checkmark_alt"
|
||||
v-if="queryAllFilterCategoryIds[subCategory.id]">
|
||||
v-if="query.categoryIds === subCategory.id">
|
||||
</f7-icon>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
@@ -360,7 +361,7 @@
|
||||
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="!query.accountIds"></f7-icon>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
<f7-list-item :class="{ 'list-item-selected': query.accountIds && queryAllFilterAccountIdsCount > 1 }" :title="$t('Multiple Accounts')" @click="showMultipleAccountsPopup()">
|
||||
<f7-list-item :class="{ 'list-item-selected': query.accountIds && queryAllFilterAccountIdsCount > 1 }" :title="$t('Multiple Accounts')" @click="filterMultipleAccounts()">
|
||||
<template #media>
|
||||
<f7-icon f7="rectangle_on_rectangle"></f7-icon>
|
||||
</template>
|
||||
@@ -369,7 +370,7 @@
|
||||
</template>
|
||||
</f7-list-item>
|
||||
<f7-list-item :title="account.name"
|
||||
:class="{ 'list-item-selected': queryAllFilterAccountIds[account.id] }"
|
||||
:class="{ 'list-item-selected': query.accountIds === account.id, 'item-in-multiple-selection': queryAllFilterAccountIdsCount > 1 && queryAllFilterAccountIds[account.id] }"
|
||||
:key="account.id"
|
||||
v-for="account in allAccounts"
|
||||
v-show="!account.hidden"
|
||||
@@ -381,7 +382,7 @@
|
||||
<template #after>
|
||||
<f7-icon class="list-item-checked-icon"
|
||||
f7="checkmark_alt"
|
||||
v-if="queryAllFilterAccountIds[account.id]">
|
||||
v-if="query.accountIds === account.id">
|
||||
</f7-icon>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
@@ -802,9 +803,11 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -812,16 +815,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.showMorePopover = false;
|
||||
@@ -880,11 +886,11 @@ export default {
|
||||
|
||||
this.reload(null);
|
||||
},
|
||||
showMultipleCategoriesPopup() {
|
||||
|
||||
filterMultipleCategories() {
|
||||
this.f7router.navigate('/settings/filter/category?type=transactionListCurrent');
|
||||
},
|
||||
showMultipleAccountsPopup() {
|
||||
|
||||
filterMultipleAccounts() {
|
||||
this.f7router.navigate('/settings/filter/account?type=transactionListCurrent');
|
||||
},
|
||||
duplicate(transaction) {
|
||||
this.f7router.navigate(`/transaction/add?id=${transaction.id}&type=${transaction.type}`);
|
||||
|
||||
Reference in New Issue
Block a user