mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-16 16:07:33 +08:00
tag filter supports selecting both included and excluded tags simultaneously
This commit is contained in:
@@ -1,26 +1,35 @@
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
import { useI18n } from '@/locales/helpers.ts';
|
||||
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
import { useTransactionsStore } from '@/stores/transaction.ts';
|
||||
import { useStatisticsStore } from '@/stores/statistics.ts';
|
||||
|
||||
import { type TypeAndDisplayName, keys, keysIfValueEquals, values } from '@/core/base.ts';
|
||||
import { entries, values } from '@/core/base.ts';
|
||||
import { TransactionTagFilterType } from '@/core/transaction.ts';
|
||||
import type { TransactionTag } from '@/models/transaction_tag.ts';
|
||||
import { TransactionTagFilter } from '@/models/transaction.ts';
|
||||
|
||||
import { objectFieldWithValueToArrayItem } from '@/lib/common.ts';
|
||||
|
||||
export enum TransactionTagFilterState {
|
||||
Default = 0,
|
||||
Include = 1,
|
||||
Exclude = 2
|
||||
}
|
||||
|
||||
export function useTransactionTagFilterSettingPageBase(type?: string) {
|
||||
const { getAllTransactionTagFilterTypes } = useI18n();
|
||||
|
||||
const transactionTagsStore = useTransactionTagsStore();
|
||||
const transactionsStore = useTransactionsStore();
|
||||
const statisticsStore = useStatisticsStore();
|
||||
|
||||
const loading = ref<boolean>(true);
|
||||
const showHidden = ref<boolean>(false);
|
||||
const filterTagIds = ref<Record<string, boolean>>({});
|
||||
const tagFilterType = ref<number>(TransactionTagFilterType.Default.type);
|
||||
const filterTagIds = ref<Record<string, TransactionTagFilterState>>({});
|
||||
const includeTagFilterType = ref<number>(TransactionTagFilterType.HasAny.type);
|
||||
const excludeTagFilterType = ref<number>(TransactionTagFilterType.NotHasAny.type);
|
||||
|
||||
const includeTagsCount = computed<number>(() => objectFieldWithValueToArrayItem(filterTagIds.value, TransactionTagFilterState.Include).length);
|
||||
const excludeTagsCount = computed<number>(() => objectFieldWithValueToArrayItem(filterTagIds.value, TransactionTagFilterState.Exclude).length);
|
||||
|
||||
const title = computed<string>(() => {
|
||||
return 'Filter Transaction Tags';
|
||||
@@ -31,7 +40,6 @@ export function useTransactionTagFilterSettingPageBase(type?: string) {
|
||||
});
|
||||
|
||||
const allTags = computed<TransactionTag[]>(() => transactionTagsStore.allTransactionTags);
|
||||
const allTagFilterTypes = computed<TypeAndDisplayName[]>(() => getAllTransactionTagFilterTypes());
|
||||
const hasAnyAvailableTag = computed<boolean>(() => transactionTagsStore.allAvailableTagsCount > 0);
|
||||
const hasAnyVisibleTag = computed<boolean>(() => {
|
||||
if (showHidden.value) {
|
||||
@@ -42,67 +50,76 @@ export function useTransactionTagFilterSettingPageBase(type?: string) {
|
||||
});
|
||||
|
||||
function loadFilterTagIds(): boolean {
|
||||
const allTransactionTagIds: Record<string, boolean> = {};
|
||||
|
||||
for (const transactionTag of values(transactionTagsStore.allTransactionTagsMap)) {
|
||||
allTransactionTagIds[transactionTag.id] = true;
|
||||
}
|
||||
let tagFilters: TransactionTagFilter[] = [];
|
||||
|
||||
if (type === 'statisticsCurrent') {
|
||||
const transactionTagIds = statisticsStore.transactionStatisticsFilter.tagIds ? statisticsStore.transactionStatisticsFilter.tagIds.split(',') : [];
|
||||
|
||||
for (const transactionTagId of transactionTagIds) {
|
||||
const transactionTag = transactionTagsStore.allTransactionTagsMap[transactionTagId];
|
||||
|
||||
if (transactionTag) {
|
||||
allTransactionTagIds[transactionTag.id] = false;
|
||||
}
|
||||
}
|
||||
filterTagIds.value = allTransactionTagIds;
|
||||
tagFilterType.value = statisticsStore.transactionStatisticsFilter.tagFilterType;
|
||||
return true;
|
||||
tagFilters = TransactionTagFilter.parse(statisticsStore.transactionStatisticsFilter.tagFilter);
|
||||
} else if (type === 'transactionListCurrent') {
|
||||
for (const transactionTagId of keysIfValueEquals(transactionsStore.allFilterTagIds, true)) {
|
||||
const transactionTag = transactionTagsStore.allTransactionTagsMap[transactionTagId];
|
||||
|
||||
if (transactionTag) {
|
||||
allTransactionTagIds[transactionTag.id] = false;
|
||||
}
|
||||
}
|
||||
filterTagIds.value = allTransactionTagIds;
|
||||
return true;
|
||||
tagFilters = TransactionTagFilter.parse(transactionsStore.transactionsFilter.tagFilter);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
const allTagIdsMap: Record<string, TransactionTagFilterState> = {};
|
||||
|
||||
for (const transactionTag of values(transactionTagsStore.allTransactionTagsMap)) {
|
||||
allTagIdsMap[transactionTag.id] = TransactionTagFilterState.Default;
|
||||
}
|
||||
|
||||
for (const tagFilter of tagFilters) {
|
||||
let state: TransactionTagFilterState = TransactionTagFilterState.Default;
|
||||
|
||||
if (tagFilter.type === TransactionTagFilterType.HasAny || tagFilter.type === TransactionTagFilterType.HasAll) {
|
||||
state = TransactionTagFilterState.Include;
|
||||
includeTagFilterType.value = tagFilter.type.type;
|
||||
} else if (tagFilter.type === TransactionTagFilterType.NotHasAny || tagFilter.type === TransactionTagFilterType.NotHasAll) {
|
||||
state = TransactionTagFilterState.Exclude;
|
||||
excludeTagFilterType.value = tagFilter.type.type;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const tagId of tagFilter.tagIds) {
|
||||
allTagIdsMap[tagId] = state;
|
||||
}
|
||||
}
|
||||
|
||||
filterTagIds.value = allTagIdsMap;
|
||||
return true;
|
||||
}
|
||||
|
||||
function saveFilterTagIds(): boolean {
|
||||
const filteredTagIds: Record<string, boolean> = {};
|
||||
let finalTagIds = '';
|
||||
const includeTagFilter: TransactionTagFilter = TransactionTagFilter.create(TransactionTagFilterType.parse(includeTagFilterType.value) ?? TransactionTagFilterType.HasAny);
|
||||
const excludeTagFilter: TransactionTagFilter = TransactionTagFilter.create(TransactionTagFilterType.parse(excludeTagFilterType.value) ?? TransactionTagFilterType.NotHasAny);
|
||||
let changed = true;
|
||||
|
||||
for (const transactionTagId of keys(filterTagIds.value)) {
|
||||
for (const [transactionTagId, state] of entries(filterTagIds.value)) {
|
||||
const transactionTag = transactionTagsStore.allTransactionTagsMap[transactionTagId];
|
||||
|
||||
if (!transactionTag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (filterTagIds.value[transactionTag.id]) {
|
||||
filteredTagIds[transactionTag.id] = true;
|
||||
} else {
|
||||
if (finalTagIds.length > 0) {
|
||||
finalTagIds += ',';
|
||||
}
|
||||
|
||||
finalTagIds += transactionTag.id;
|
||||
if (state === TransactionTagFilterState.Include) {
|
||||
includeTagFilter.tagIds.push(transactionTag.id);
|
||||
} else if (state === TransactionTagFilterState.Exclude) {
|
||||
excludeTagFilter.tagIds.push(transactionTag.id);
|
||||
}
|
||||
}
|
||||
|
||||
const tagFilters: TransactionTagFilter[] = [];
|
||||
|
||||
if (includeTagFilter.tagIds.length > 0) {
|
||||
tagFilters.push(includeTagFilter);
|
||||
}
|
||||
|
||||
if (excludeTagFilter.tagIds.length > 0) {
|
||||
tagFilters.push(excludeTagFilter);
|
||||
}
|
||||
|
||||
if (type === 'statisticsCurrent') {
|
||||
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||
tagIds: finalTagIds,
|
||||
tagFilterType: tagFilterType.value
|
||||
tagFilter: TransactionTagFilter.toTextualTagFilters(tagFilters)
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
@@ -110,7 +127,7 @@ export function useTransactionTagFilterSettingPageBase(type?: string) {
|
||||
}
|
||||
} else if (type === 'transactionListCurrent') {
|
||||
changed = transactionsStore.updateTransactionListFilter({
|
||||
tagIds: finalTagIds
|
||||
tagFilter: TransactionTagFilter.toTextualTagFilters(tagFilters)
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
@@ -126,12 +143,14 @@ export function useTransactionTagFilterSettingPageBase(type?: string) {
|
||||
loading,
|
||||
showHidden,
|
||||
filterTagIds,
|
||||
tagFilterType,
|
||||
includeTagFilterType,
|
||||
excludeTagFilterType,
|
||||
// computed states
|
||||
includeTagsCount,
|
||||
excludeTagsCount,
|
||||
title,
|
||||
applyText,
|
||||
allTags,
|
||||
allTagFilterTypes,
|
||||
hasAnyAvailableTag,
|
||||
hasAnyVisibleTag,
|
||||
// functions
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
import { type TransactionListFilter, type TransactionMonthList, useTransactionsStore } from '@/stores/transaction.ts';
|
||||
|
||||
import { type TypeAndName, entries } from '@/core/base.ts';
|
||||
import { type TypeAndName, keys, entries } from '@/core/base.ts';
|
||||
import type { NumeralSystem } from '@/core/numeral.ts';
|
||||
import { type TextualYearMonthDay, type Year0BasedMonth, type LocalizedDateRange, type WeekDayValue, DateRange, DateRangeScene } from '@/core/datetime.ts';
|
||||
import { AccountType } from '@/core/account.ts';
|
||||
@@ -19,7 +19,7 @@ import { DISPLAY_HIDDEN_AMOUNT, INCOMPLETE_AMOUNT_SUFFIX } from '@/consts/numera
|
||||
import type { Account } from '@/models/account.ts';
|
||||
import type { TransactionCategory } from '@/models/transaction_category.ts';
|
||||
import type { TransactionTag } from '@/models/transaction_tag.ts';
|
||||
import type { Transaction } from '@/models/transaction.ts';
|
||||
import { type Transaction, TransactionTagFilter } from '@/models/transaction.ts';
|
||||
|
||||
import {
|
||||
getUtcOffsetByUtcOffsetMinutes,
|
||||
@@ -196,7 +196,7 @@ export function useTransactionListPageBase() {
|
||||
});
|
||||
|
||||
const queryTagName = computed<string>(() => {
|
||||
if (query.value.tagIds === 'none') {
|
||||
if (query.value.tagFilter === TransactionTagFilter.TransactionNoTagFilterValue) {
|
||||
return tt('Without Tags');
|
||||
}
|
||||
|
||||
@@ -204,7 +204,15 @@ export function useTransactionListPageBase() {
|
||||
return tt('Multiple Tags');
|
||||
}
|
||||
|
||||
return allTransactionTags.value[query.value.tagIds]?.name || tt('Tags');
|
||||
for (const tagId of keys(queryAllFilterTagIds.value)) {
|
||||
const tagName = allTransactionTags.value[tagId]?.name;
|
||||
|
||||
if (tagName) {
|
||||
return tagName;
|
||||
}
|
||||
}
|
||||
|
||||
return tt('Tags');
|
||||
});
|
||||
|
||||
const queryAmount = computed<string>(() => {
|
||||
|
||||
Reference in New Issue
Block a user