tag filter supports selecting both included and excluded tags simultaneously

This commit is contained in:
MaysWind
2025-11-24 02:12:44 +08:00
parent 45be96cf68
commit 6430a52027
45 changed files with 1151 additions and 706 deletions
@@ -11,22 +11,30 @@
<v-menu activator="parent">
<v-list>
<v-list-item :prepend-icon="mdiSelectAll"
:title="tt('Select All')"
:title="tt('Set All to Included')"
:disabled="!hasAnyVisibleTag"
@click="selectAllTransactionTags"></v-list-item>
@click="setAllToState(false, TransactionTagFilterState.Include)"></v-list-item>
<v-list-item :prepend-icon="mdiSelect"
:title="tt('Select None')"
:title="tt('Set All to Default')"
:disabled="!hasAnyVisibleTag"
@click="selectNoneTransactionTags"></v-list-item>
@click="setAllToState(false, TransactionTagFilterState.Default)"></v-list-item>
<v-list-item :prepend-icon="mdiSelectInverse"
:title="tt('Invert Selection')"
:title="tt('Set All to Excluded')"
:disabled="!hasAnyVisibleTag"
@click="selectInvertTransactionTags"></v-list-item>
@click="setAllToState(false, TransactionTagFilterState.Exclude)"></v-list-item>
<v-divider class="my-2"/>
<v-list-item :prepend-icon="mdiSelectAll"
:title="tt('Select All Visible')"
:title="tt('Set All Visible Items to Included')"
:disabled="!hasAnyVisibleTag"
@click="selectAllVisibleTransactionTags"></v-list-item>
@click="setAllToState(true, TransactionTagFilterState.Include)"></v-list-item>
<v-list-item :prepend-icon="mdiSelectAll"
:title="tt('Set All Visible Items to Default')"
:disabled="!hasAnyVisibleTag"
@click="setAllToState(true, TransactionTagFilterState.Default)"></v-list-item>
<v-list-item :prepend-icon="mdiSelectAll"
:title="tt('Set All Visible Items to Excluded')"
:disabled="!hasAnyVisibleTag"
@click="setAllToState(true, TransactionTagFilterState.Exclude)"></v-list-item>
<v-divider class="my-2"/>
<v-list-item :prepend-icon="mdiEyeOutline"
:title="tt('Show Hidden Transaction Tags')"
@@ -47,22 +55,30 @@
<v-menu activator="parent">
<v-list>
<v-list-item :prepend-icon="mdiSelectAll"
:title="tt('Select All')"
:title="tt('Set All to Included')"
:disabled="!hasAnyVisibleTag"
@click="selectAllTransactionTags"></v-list-item>
@click="setAllToState(false, TransactionTagFilterState.Include)"></v-list-item>
<v-list-item :prepend-icon="mdiSelect"
:title="tt('Select None')"
:title="tt('Set All to Default')"
:disabled="!hasAnyVisibleTag"
@click="selectNoneTransactionTags"></v-list-item>
@click="setAllToState(false, TransactionTagFilterState.Default)"></v-list-item>
<v-list-item :prepend-icon="mdiSelectInverse"
:title="tt('Invert Selection')"
:title="tt('Set All to Excluded')"
:disabled="!hasAnyVisibleTag"
@click="selectInvertTransactionTags"></v-list-item>
@click="setAllToState(false, TransactionTagFilterState.Exclude)"></v-list-item>
<v-divider class="my-2"/>
<v-list-item :prepend-icon="mdiSelectAll"
:title="tt('Select All Visible')"
:title="tt('Set All Visible Items to Included')"
:disabled="!hasAnyVisibleTag"
@click="selectAllVisibleTransactionTags"></v-list-item>
@click="setAllToState(true, TransactionTagFilterState.Include)"></v-list-item>
<v-list-item :prepend-icon="mdiSelectAll"
:title="tt('Set All Visible Items to Default')"
:disabled="!hasAnyVisibleTag"
@click="setAllToState(true, TransactionTagFilterState.Default)"></v-list-item>
<v-list-item :prepend-icon="mdiSelectAll"
:title="tt('Set All Visible Items to Excluded')"
:disabled="!hasAnyVisibleTag"
@click="setAllToState(true, TransactionTagFilterState.Exclude)"></v-list-item>
<v-divider class="my-2"/>
<v-list-item :prepend-icon="mdiEyeOutline"
:title="tt('Show Hidden Transaction Tags')"
@@ -86,15 +102,24 @@
</v-card-text>
<v-card-text :class="{ 'mt-0 mt-sm-2 mt-md-4': dialogMode }" v-else-if="!loading && hasAnyVisibleTag">
<div class="tag-filter-types d-flex flex-column mb-4" v-if="type === 'statisticsCurrent'">
<v-btn border class="justify-start" :key="filterType.type"
:color="tagFilterType === filterType.type ? 'primary' : 'default'"
:variant="tagFilterType === filterType.type ? 'tonal' : 'outlined'"
:append-icon="(tagFilterType === filterType.type ? mdiCheck : undefined)"
v-for="filterType in allTagFilterTypes"
@click="tagFilterType = filterType.type">
{{ filterType.displayName }}
</v-btn>
<div class="mb-4" v-if="includeTagsCount > 1">
<v-btn-toggle class="toggle-buttons" density="compact" variant="outlined"
mandatory="force" divided
:model-value="includeTagFilterType"
@update:model-value="updateTransactionTagIncludeType($event)">
<v-btn :value="TransactionTagFilterType.HasAny.type">{{ tt(TransactionTagFilterType.HasAny.name) }}</v-btn>
<v-btn :value="TransactionTagFilterType.HasAll.type">{{ tt(TransactionTagFilterType.HasAll.name) }}</v-btn>
</v-btn-toggle>
</div>
<div class="mb-4" v-if="excludeTagsCount > 1">
<v-btn-toggle class="toggle-buttons" density="compact" variant="outlined"
mandatory="force" divided
:model-value="excludeTagFilterType"
@update:model-value="updateTransactionTagExcludeType($event)">
<v-btn :value="TransactionTagFilterType.NotHasAny.type">{{ tt(TransactionTagFilterType.NotHasAny.name) }}</v-btn>
<v-btn :value="TransactionTagFilterType.NotHasAll.type">{{ tt(TransactionTagFilterType.NotHasAll.name) }}</v-btn>
</v-btn-toggle>
</div>
<v-expansion-panels class="tag-categories" multiple v-model="expandTagCategories">
@@ -108,18 +133,23 @@
v-for="transactionTag in allTags">
<v-list-item v-if="showHidden || !transactionTag.hidden">
<template #prepend>
<v-checkbox :model-value="!filterTagIds[transactionTag.id]"
@update:model-value="updateTransactionTagSelected(transactionTag, $event)">
<template #label>
<v-badge class="right-bottom-icon" color="secondary"
location="bottom right" offset-x="2" offset-y="2" :icon="mdiEyeOffOutline"
v-if="transactionTag.hidden">
<v-icon size="24" :icon="mdiPound"/>
</v-badge>
<v-icon size="24" :icon="mdiPound" v-else-if="!transactionTag.hidden"/>
<span class="ms-3">{{ transactionTag.name }}</span>
</template>
</v-checkbox>
<v-badge class="right-bottom-icon" color="secondary"
location="bottom right" offset-x="2" offset-y="2" :icon="mdiEyeOffOutline"
v-if="transactionTag.hidden">
<v-icon size="24" :icon="mdiPound"/>
</v-badge>
<v-icon size="24" :icon="mdiPound" v-else-if="!transactionTag.hidden"/>
<span class="ms-3">{{ transactionTag.name }}</span>
</template>
<template #append>
<v-btn-toggle class="toggle-buttons" density="compact" variant="outlined"
mandatory="force" divided
:model-value="filterTagIds[transactionTag.id]"
@update:model-value="updateTransactionTagState(transactionTag, $event)">
<v-btn :value="TransactionTagFilterState.Include">{{ tt('Included') }}</v-btn>
<v-btn :value="TransactionTagFilterState.Default">{{ tt('Default') }}</v-btn>
<v-btn :value="TransactionTagFilterState.Exclude">{{ tt('Excluded') }}</v-btn>
</v-btn-toggle>
</template>
</v-list-item>
</template>
@@ -146,19 +176,16 @@ import SnackBar from '@/components/desktop/SnackBar.vue';
import { ref, useTemplateRef } from 'vue';
import { useI18n } from '@/locales/helpers.ts';
import { useTransactionTagFilterSettingPageBase } from '@/views/base/settings/TransactionTagFilterSettingPageBase.ts';
import {
useTransactionTagFilterSettingPageBase,
TransactionTagFilterState
} from '@/views/base/settings/TransactionTagFilterSettingPageBase.ts';
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
import { TransactionTagFilterType } from '@/core/transaction.ts';
import type { TransactionTag } from '@/models/transaction_tag.ts';
import {
selectAllVisible,
selectAll,
selectNone,
selectInvert
} from '@/lib/common.ts';
import {
mdiSelectAll,
mdiSelect,
@@ -166,7 +193,6 @@ import {
mdiEyeOutline,
mdiEyeOffOutline,
mdiDotsVertical,
mdiCheck,
mdiPound
} from '@mdi/js';
@@ -188,11 +214,13 @@ const {
loading,
showHidden,
filterTagIds,
tagFilterType,
includeTagFilterType,
excludeTagFilterType,
includeTagsCount,
excludeTagsCount,
title,
applyText,
allTags,
allTagFilterTypes,
hasAnyAvailableTag,
hasAnyVisibleTag,
loadFilterTagIds,
@@ -223,40 +251,38 @@ function init(): void {
});
}
function updateTransactionTagSelected(transactionTag: TransactionTag, value: boolean | null): void {
filterTagIds.value[transactionTag.id] = !value;
function updateTransactionTagState(transactionTag: TransactionTag, value: TransactionTagFilterState): void {
filterTagIds.value[transactionTag.id] = value;
if (props.autoSave) {
save();
}
}
function selectAllTransactionTags(): void {
selectAll(filterTagIds.value, transactionTagsStore.allTransactionTagsMap);
function updateTransactionTagIncludeType(value: number): void {
includeTagFilterType.value = value;
if (props.autoSave) {
save();
}
}
function selectNoneTransactionTags(): void {
selectNone(filterTagIds.value, transactionTagsStore.allTransactionTagsMap);
function updateTransactionTagExcludeType(value: number): void {
excludeTagFilterType.value = value;
if (props.autoSave) {
save();
}
}
function selectInvertTransactionTags(): void {
selectInvert(filterTagIds.value, transactionTagsStore.allTransactionTagsMap);
function setAllToState(onlyVisible: boolean, value: TransactionTagFilterState): void {
for (const tag of allTags.value) {
if (onlyVisible && !showHidden.value && tag.hidden) {
continue;
}
if (props.autoSave) {
save();
filterTagIds.value[tag.id] = value;
}
}
function selectAllVisibleTransactionTags(): void {
selectAllVisible(filterTagIds.value, transactionTagsStore.allTransactionTagsMap);
if (props.autoSave) {
save();
@@ -276,15 +302,9 @@ init();
</script>
<style>
.tag-filter-types .v-btn:not(:first-child) {
border-top-left-radius: inherit;
border-top-right-radius: inherit;
}
.tag-filter-types .v-btn:not(:last-child) {
border-bottom: 0;
border-bottom-left-radius: inherit;
border-bottom-right-radius: inherit;
.tag-categories .tag-filter-state-toggle {
overflow-x: auto;
white-space: nowrap;
}
.tag-categories .v-expansion-panel-text__wrapper {
@@ -576,8 +576,7 @@ interface TransactionStatisticsProps {
initEndTime?: TextualYearMonth | '',
initFilterAccountIds?: string,
initFilterCategoryIds?: string,
initTagIds?: string,
initTagFilterType?: string,
initTagFilter?: string,
initKeyword?: string;
initSortingType?: string,
initTrendDateAggregationType?: string
@@ -757,8 +756,7 @@ function init(initProps: TransactionStatisticsProps): void {
chartDataType: initProps.initChartDataType ? parseInt(initProps.initChartDataType) : undefined,
filterAccountIds: initProps.initFilterAccountIds ? arrayItemToObjectField(initProps.initFilterAccountIds.split(','), true) : {},
filterCategoryIds: initProps.initFilterCategoryIds ? arrayItemToObjectField(initProps.initFilterCategoryIds.split(','), true) : {},
tagIds: initProps.initTagIds,
tagFilterType: initProps.initTagFilterType && parseInt(initProps.initTagFilterType) >= 0 ? parseInt(initProps.initTagFilterType) : undefined,
tagFilter: initProps.initTagFilter,
keyword: initProps.initKeyword,
sortingType: initProps.initSortingType ? parseInt(initProps.initSortingType) : undefined
};
@@ -1314,8 +1312,7 @@ onBeforeRouteUpdate((to) => {
initEndTime: (to.query['endTime'] as TextualYearMonth | null) || undefined,
initFilterAccountIds: (to.query['filterAccountIds'] as string | null) || undefined,
initFilterCategoryIds: (to.query['filterCategoryIds'] as string | null) || undefined,
initTagIds: (to.query['tagIds'] as string | null) || undefined,
initTagFilterType: (to.query['tagFilterType'] as string | null) || undefined,
initTagFilter: (to.query['tagFilter'] as string | null) || undefined,
initKeyword: (to.query['keyword'] as string | null) || undefined,
initSortingType: (to.query['sortingType'] as string | null) || undefined,
initTrendDateAggregationType: (to.query['trendDateAggregationType'] as string | null) || undefined,
+38 -92
View File
@@ -431,15 +431,15 @@
@update:model-value="scrollTagMenuToSelectedItem">
<template #activator="{ props }">
<div class="d-flex align-center cursor-pointer"
:class="{ 'readonly': loading, 'text-primary': query.tagIds }" v-bind="props">
:class="{ 'readonly': loading, 'text-primary': query.tagFilter }" v-bind="props">
<span>{{ queryTagName }}</span>
<v-icon :icon="mdiMenuDown" />
</div>
</template>
<v-list :selected="[queryAllSelectedFilterTagIds]">
<v-list-item key="" value="" class="text-sm" density="compact"
:class="{ 'list-item-selected': !query.tagIds }"
:append-icon="(!query.tagIds ? mdiCheck : undefined)">
:class="{ 'list-item-selected': !query.tagFilter }"
:append-icon="(!query.tagFilter ? mdiCheck : undefined)">
<v-list-item-title class="cursor-pointer"
@click="changeTagFilter('')">
<div class="d-flex align-center">
@@ -448,11 +448,13 @@
</div>
</v-list-item-title>
</v-list-item>
<v-list-item key="none" value="none" class="text-sm" density="compact"
:class="{ 'list-item-selected': query.tagIds === 'none' }"
:append-icon="(query.tagIds === 'none' ? mdiCheck : undefined)">
<v-list-item class="text-sm" density="compact"
:key="TransactionTagFilter.TransactionNoTagFilterValue"
:value="TransactionTagFilter.TransactionNoTagFilterValue"
:class="{ 'list-item-selected': query.tagFilter === TransactionTagFilter.TransactionNoTagFilterValue }"
:append-icon="(query.tagFilter === TransactionTagFilter.TransactionNoTagFilterValue ? mdiCheck : undefined)">
<v-list-item-title class="cursor-pointer"
@click="changeTagFilter('none')">
@click="changeTagFilter(TransactionTagFilter.TransactionNoTagFilterValue)">
<div class="d-flex align-center">
<v-icon :icon="mdiBorderNoneVariant" />
<span class="text-sm ms-3">{{ tt('Without Tags') }}</span>
@@ -460,8 +462,8 @@
</v-list-item-title>
</v-list-item>
<v-list-item key="multiple" value="multiple" class="text-sm" density="compact"
:class="{ 'list-item-selected': query.tagIds && queryAllFilterTagIdsCount > 1 }"
:append-icon="(query.tagIds && queryAllFilterTagIdsCount > 1 ? mdiCheck : undefined)"
:class="{ 'list-item-selected': query.tagFilter && queryAllFilterTagIdsCount > 1 }"
:append-icon="(query.tagFilter && queryAllFilterTagIdsCount > 1 ? mdiCheck : undefined)"
v-if="allAvailableTagsCount > 0">
<v-list-item-title class="cursor-pointer"
@click="showFilterTagDialog = true">
@@ -472,34 +474,18 @@
</v-list-item-title>
</v-list-item>
<v-divider v-if="query.tagIds && query.tagIds !== 'none'" />
<template v-if="query.tagIds && query.tagIds !== 'none'">
<v-list-item class="text-sm" density="compact"
:key="filterType.type"
:value="filterType.type"
:append-icon="(query.tagFilterType === filterType.type ? mdiCheck : undefined)"
v-for="filterType in allTransactionTagFilterTypes">
<v-list-item-title class="cursor-pointer"
@click="changeTagFilterType(filterType.type)">
<div class="d-flex align-center">
<v-icon size="24" :icon="filterType.icon"/>
<span class="text-sm ms-3">{{ filterType.displayName }}</span>
</div>
</v-list-item-title>
</v-list-item>
</template>
<v-divider v-if="query.tagFilter && query.tagFilter !== TransactionTagFilter.TransactionNoTagFilterValue" />
<template :key="transactionTag.id"
v-for="transactionTag in allTransactionTags">
<v-divider v-if="!transactionTag.hidden || query.tagIds === transactionTag.id" />
<v-divider v-if="!transactionTag.hidden || isDefined(queryAllFilterTagIds[transactionTag.id])" />
<v-list-item class="text-sm" density="compact"
:value="transactionTag.id"
:class="{ 'list-item-selected': query.tagIds === transactionTag.id, 'item-in-multiple-selection': queryAllFilterTagIdsCount > 1 && queryAllFilterTagIds[transactionTag.id] }"
:append-icon="(query.tagIds === transactionTag.id ? mdiCheck : undefined)"
v-if="!transactionTag.hidden || query.tagIds === transactionTag.id">
:class="{ 'list-item-selected': queryAllFilterTagIdsCount === 1 && isDefined(queryAllFilterTagIds[transactionTag.id]), 'item-in-multiple-selection': queryAllFilterTagIdsCount > 1 && isDefined(queryAllFilterTagIds[transactionTag.id]) }"
:append-icon="(queryAllFilterTagIds[transactionTag.id] === true ? mdiCheck : (queryAllFilterTagIds[transactionTag.id] === false ? mdiClose : undefined))"
v-if="!transactionTag.hidden || isDefined(queryAllFilterTagIds[transactionTag.id])">
<v-list-item-title class="cursor-pointer"
@click="changeTagFilter(transactionTag.id)">
@click="changeTagFilter(TransactionTagFilter.of(transactionTag.id).toTextualTagFilter())">
<div class="d-flex align-center">
<v-icon size="24" :icon="mdiPound"/>
<span class="text-sm ms-3">{{ transactionTag.name }}</span>
@@ -678,7 +664,6 @@ import { useDesktopPageStore } from '@/stores/desktopPage.ts';
import {
type NameNumeralValue,
type TypeAndDisplayName,
keys
} from '@/core/base.ts';
import {
@@ -690,16 +675,18 @@ import {
} from '@/core/datetime.ts';
import { type NumeralSystem, AmountFilterType } from '@/core/numeral.ts';
import { ThemeType } from '@/core/theme.ts';
import { TransactionType, TransactionTagFilterType } from '@/core/transaction.ts';
import { TransactionType } from '@/core/transaction.ts';
import { TemplateType } from '@/core/template.ts';
import type { TransactionCategory } from '@/models/transaction_category.ts';
import type { Transaction } from '@/models/transaction.ts';
import { type Transaction, TransactionTagFilter } from '@/models/transaction.ts';
import type { TransactionTemplate } from '@/models/transaction_template.ts';
import {
isDefined,
isObject,
isString,
isNumber
isNumber,
objectFieldWithValueToArrayItem
} from '@/lib/common.ts';
import {
getCurrentUnixTime,
@@ -731,6 +718,7 @@ import logger from '@/lib/logger.ts';
import {
mdiMagnify,
mdiCheck,
mdiClose,
mdiViewGridOutline,
mdiBorderNoneVariant,
mdiVectorArrangeBelow,
@@ -740,10 +728,6 @@ import {
mdiPencilBoxOutline,
mdiArrowLeft,
mdiArrowRight,
mdiPlusBoxMultipleOutline,
mdiCheckboxMultipleOutline,
mdiMinusBoxMultipleOutline,
mdiCloseBoxMultipleOutline,
mdiPound,
mdiMagicStaff,
mdiTextBoxOutline
@@ -757,8 +741,7 @@ interface TransactionListProps {
initType?: string,
initCategoryIds?: string,
initAccountIds?: string,
initTagIds?: string,
initTagFilterType?: string,
initTagFilter?: string,
initAmountFilter?: string,
initKeyword?: string
}
@@ -771,12 +754,6 @@ type EditDialogType = InstanceType<typeof EditDialog>;
type AIImageRecognitionDialogType = InstanceType<typeof AIImageRecognitionDialog>;
type ImportDialogType = InstanceType<typeof ImportDialog>;
interface TransactionTemplateWithIcon {
type: number;
displayName: string;
icon: string;
}
interface TransactionListDisplayTotalAmount {
income: string;
expense: string;
@@ -789,7 +766,6 @@ const theme = useTheme();
const {
tt,
getAllRecentMonthDateRanges,
getAllTransactionTagFilterTypes,
getWeekdayLongName,
getCurrentNumeralSystemType
} = useI18n();
@@ -852,13 +828,6 @@ const transactionsStore = useTransactionsStore();
const transactionTemplatesStore = useTransactionTemplatesStore();
const desktopPageStore = useDesktopPageStore();
const tagFilterIconMap: Record<number, string> = {
[TransactionTagFilterType.HasAny.type]: mdiPlusBoxMultipleOutline,
[TransactionTagFilterType.HasAll.type]: mdiCheckboxMultipleOutline,
[TransactionTagFilterType.NotHasAny.type]: mdiMinusBoxMultipleOutline,
[TransactionTagFilterType.NotHasAll.type]: mdiCloseBoxMultipleOutline
};
const timeFilterMenu = useTemplateRef<VMenu>('timeFilterMenu');
const categoryFilterMenu = useTemplateRef<VMenu>('categoryFilterMenu');
const amountFilterMenu = useTemplateRef<VMenu>('amountFilterMenu');
@@ -912,21 +881,6 @@ const allTransactionTemplates = computed<TransactionTemplate[]>(() => {
return allTemplates[TemplateType.Normal.type] || [];
});
const allTransactionTagFilterTypes = computed<TransactionTemplateWithIcon[]>(() => {
const allTagFilterTypes: TypeAndDisplayName[] = getAllTransactionTagFilterTypes();
const allTagFilterTypesWithIcon: TransactionTemplateWithIcon[] = [];
for (const tagFilterType of allTagFilterTypes) {
allTagFilterTypesWithIcon.push({
type: tagFilterType.type,
displayName: tagFilterType.displayName,
icon: tagFilterIconMap[tagFilterType.type] ?? ''
});
}
return allTagFilterTypesWithIcon;
});
const allowCategoryTypes = computed<string>(() => {
if (TransactionType.Income <= query.value.type && query.value.type <= TransactionType.Transfer) {
return transactionTypeToCategoryType(query.value.type)?.toString() ?? '';
@@ -1018,10 +972,16 @@ const queryAllSelectedFilterAccountIds = computed<string>(() => {
});
const queryAllSelectedFilterTagIds = computed<string>(() => {
if (queryAllFilterTagIdsCount.value === 0) {
if (query.value.tagFilter === TransactionTagFilter.TransactionNoTagFilterValue) {
return TransactionTagFilter.TransactionNoTagFilterValue;
} else if (queryAllFilterTagIdsCount.value === 0) {
return '';
} else if (queryAllFilterTagIdsCount.value === 1) {
return query.value.tagIds;
for (const tagId of keys(queryAllFilterTagIds.value)) {
return tagId;
}
return '';
} else { // queryAllFilterTagIdsCount.value > 1
return 'multiple';
}
@@ -1147,8 +1107,7 @@ function init(initProps: TransactionListProps): void {
type: initProps.initType && parseInt(initProps.initType) > 0 ? parseInt(initProps.initType) : undefined,
categoryIds: initProps.initCategoryIds,
accountIds: initProps.initAccountIds,
tagIds: initProps.initTagIds,
tagFilterType: initProps.initTagFilterType && parseInt(initProps.initTagFilterType) >= 0 ? parseInt(initProps.initTagFilterType) : undefined,
tagFilter: initProps.initTagFilter,
amountFilter: initProps.initAmountFilter || '',
keyword: initProps.initKeyword || ''
});
@@ -1490,13 +1449,13 @@ function changeMultipleAccountsFilter(changed: boolean): void {
updateUrlWhenChanged(changed);
}
function changeTagFilter(tagIds: string): void {
if (query.value.tagIds === tagIds) {
function changeTagFilter(tagFilter: string): void {
if (query.value.tagFilter === tagFilter) {
return;
}
const changed = transactionsStore.updateTransactionListFilter({
tagIds: tagIds
tagFilter: tagFilter
});
updateUrlWhenChanged(changed);
@@ -1508,18 +1467,6 @@ function changeMultipleTagsFilter(changed: boolean): void {
updateUrlWhenChanged(changed);
}
function changeTagFilterType(filterType: number): void {
if (query.value.tagFilterType === filterType) {
return;
}
const changed = transactionsStore.updateTransactionListFilter({
tagFilterType: filterType
});
updateUrlWhenChanged(changed);
}
function changeKeywordFilter(keyword: string): void {
if (query.value.keyword === keyword) {
return;
@@ -1592,7 +1539,7 @@ function add(template?: TransactionTemplate): void {
type: query.value.type,
categoryId: queryAllFilterCategoryIdsCount.value === 1 ? query.value.categoryIds : '',
accountId: queryAllFilterAccountIdsCount.value === 1 ? query.value.accountIds : '',
tagIds: query.value.tagIds || '',
tagIds: objectFieldWithValueToArrayItem(queryAllFilterTagIds.value, true).join(',') || '',
template: template
}).then(result => {
if (result && result.message) {
@@ -1765,8 +1712,7 @@ onBeforeRouteUpdate((to) => {
initType: (to.query['type'] as string | null) || undefined,
initCategoryIds: (to.query['categoryIds'] as string | null) || undefined,
initAccountIds: (to.query['accountIds'] as string | null) || undefined,
initTagIds: (to.query['tagIds'] as string | null) || undefined,
initTagFilterType: (to.query['tagFilterType'] as string | null) || undefined,
initTagFilter: (to.query['tagFilter'] as string | null) || undefined,
initAmountFilter: (to.query['amountFilter'] as string | null) || undefined,
initKeyword: (to.query['keyword'] as string | null) || undefined
});
@@ -57,7 +57,7 @@
v-for="typeName in parsedFileAllTransactionTypes">
<td>{{ typeName }}</td>
<td>
<v-btn-toggle class="transaction-types-toggle" density="compact" variant="outlined"
<v-btn-toggle class="toggle-buttons" density="compact" variant="outlined"
mandatory="force" divided
v-model="parsedFileDataColumnMapping.transactionTypeMapping[typeName]">
<v-btn :value="undefined">{{ tt('None') }}</v-btn>
@@ -166,14 +166,14 @@
v-for="separator in allSeparators">
<td>{{ separator.name }} ({{separator.value}})</td>
<td>
<v-btn-toggle class="transaction-types-toggle" density="compact" variant="outlined"
<v-btn-toggle class="toggle-buttons" density="compact" variant="outlined"
mandatory="force" divided
v-model="parsedFileDataColumnMapping.geoLocationOrder"
v-if="parsedFileDataColumnMapping.geoLocationSeparator === separator.value">
<v-btn value="latlon">{{ `${tt('Latitude')}${separator.value}${tt('Longitude')}` }}</v-btn>
<v-btn value="lonlat">{{ `${tt('Longitude')}${separator.value}${tt('Latitude')}` }}</v-btn>
</v-btn-toggle>
<v-btn-group class="transaction-types-toggle" density="compact" variant="outlined"
<v-btn-group class="toggle-buttons" density="compact" variant="outlined"
divided v-if="parsedFileDataColumnMapping.geoLocationSeparator !== separator.value">
<v-btn @click="parsedFileDataColumnMapping.setGeoLocationFormat(separator.value, 'latlon')">{{ `${tt('Latitude')}${separator.value}${tt('Longitude')}` }}</v-btn>
<v-btn @click="parsedFileDataColumnMapping.setGeoLocationFormat(separator.value, 'lonlat')">{{ `${tt('Longitude')}${separator.value}${tt('Latitude')}` }}</v-btn>
@@ -552,39 +552,3 @@ defineExpose({
saveColumnMappingFile
});
</script>
<style>
.transaction-types-popup-menu .transaction-types-toggle {
overflow-x: auto;
white-space: nowrap;
}
.transaction-types-popup-menu .transaction-types-toggle.v-btn-toggle {
height: auto !important;
padding: 0;
border: none;
}
.transaction-types-popup-menu .transaction-types-toggle.v-btn-toggle > .v-btn {
border-color: rgba(var(--v-border-color), var(--v-border-opacity));
}
.transaction-types-popup-menu .transaction-types-toggle.v-btn-toggle > .v-btn:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-left: none;
}
.transaction-types-popup-menu .transaction-types-toggle.v-btn-toggle > .v-btn:not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.transaction-types-popup-menu .transaction-types-toggle.v-btn-toggle > .v-btn {
border: thin solid rgba(var(--v-border-color), var(--v-border-opacity));
}
.transaction-types-popup-menu .transaction-types-toggle.v-btn-toggle button.v-btn {
width: auto !important;
}
</style>