mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-19 17:24:26 +08:00
add transaction tag filter to frontend
This commit is contained in:
@@ -425,6 +425,48 @@ export function categorizedArrayToPlainArray(object) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectAll(filterItemIds, allItemsMap) {
|
||||||
|
for (let itemId in filterItemIds) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(filterItemIds, itemId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = allItemsMap[itemId];
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
filterItemIds[item.id] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectNone(filterItemIds, allItemsMap) {
|
||||||
|
for (let itemId in filterItemIds) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(filterItemIds, itemId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = allItemsMap[itemId];
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
filterItemIds[item.id] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectInvert(filterItemIds, allItemsMap) {
|
||||||
|
for (let itemId in filterItemIds) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(filterItemIds, itemId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = allItemsMap[itemId];
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
filterItemIds[item.id] = !filterItemIds[item.id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function isPrimaryItemHasSecondaryValue(primaryItem, primarySubItemsField, secondaryValueField, secondaryValue) {
|
export function isPrimaryItemHasSecondaryValue(primaryItem, primarySubItemsField, secondaryValueField, secondaryValue) {
|
||||||
for (let i = 0; i < primaryItem[primarySubItemsField].length; i++) {
|
for (let i = 0; i < primaryItem[primarySubItemsField].length; i++) {
|
||||||
const secondaryItem = primaryItem[primarySubItemsField][i];
|
const secondaryItem = primaryItem[primarySubItemsField][i];
|
||||||
|
|||||||
+4
-4
@@ -282,15 +282,15 @@ export default {
|
|||||||
id
|
id
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getTransactions: ({ maxTime, minTime, count, page, withCount, type, categoryIds, accountIds, amountFilter, keyword }) => {
|
getTransactions: ({ maxTime, minTime, count, page, withCount, type, categoryIds, accountIds, tagIds, amountFilter, keyword }) => {
|
||||||
amountFilter = encodeURIComponent(amountFilter);
|
amountFilter = encodeURIComponent(amountFilter);
|
||||||
keyword = encodeURIComponent(keyword);
|
keyword = encodeURIComponent(keyword);
|
||||||
return axios.get(`v1/transactions/list.json?max_time=${maxTime}&min_time=${minTime}&type=${type}&category_ids=${categoryIds}&account_ids=${accountIds}&amount_filter=${amountFilter}&keyword=${keyword}&count=${count}&page=${page}&with_count=${withCount}&trim_account=true&trim_category=true&trim_tag=true`);
|
return axios.get(`v1/transactions/list.json?max_time=${maxTime}&min_time=${minTime}&type=${type}&category_ids=${categoryIds}&account_ids=${accountIds}&tag_ids=${tagIds}&amount_filter=${amountFilter}&keyword=${keyword}&count=${count}&page=${page}&with_count=${withCount}&trim_account=true&trim_category=true&trim_tag=true`);
|
||||||
},
|
},
|
||||||
getAllTransactionsByMonth: ({ year, month, type, categoryIds, accountIds, amountFilter, keyword }) => {
|
getAllTransactionsByMonth: ({ year, month, type, categoryIds, accountIds, tagIds, amountFilter, keyword }) => {
|
||||||
amountFilter = encodeURIComponent(amountFilter);
|
amountFilter = encodeURIComponent(amountFilter);
|
||||||
keyword = encodeURIComponent(keyword);
|
keyword = encodeURIComponent(keyword);
|
||||||
return axios.get(`v1/transactions/list/by_month.json?year=${year}&month=${month}&type=${type}&category_ids=${categoryIds}&account_ids=${accountIds}&amount_filter=${amountFilter}&keyword=${keyword}&trim_account=true&trim_category=true&trim_tag=true`);
|
return axios.get(`v1/transactions/list/by_month.json?year=${year}&month=${month}&type=${type}&category_ids=${categoryIds}&account_ids=${accountIds}&tag_ids=${tagIds}&amount_filter=${amountFilter}&keyword=${keyword}&trim_account=true&trim_category=true&trim_tag=true`);
|
||||||
},
|
},
|
||||||
getTransactionStatistics: ({ startTime, endTime, useTransactionTimezone }) => {
|
getTransactionStatistics: ({ startTime, endTime, useTransactionTimezone }) => {
|
||||||
const queryParams = [];
|
const queryParams = [];
|
||||||
|
|||||||
+17
-1
@@ -22,7 +22,7 @@ function getDisplayAmount(amount, currency, hideAmount, formatAmountWithCurrency
|
|||||||
return formatAmountWithCurrencyFunc(amount, currency);
|
return formatAmountWithCurrencyFunc(amount, currency);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setTransactionModelByTransaction(transaction, transaction2, allCategories, allCategoriesMap, allVisibleAccounts, allAccountsMap, defaultAccountId, options, setContextData, convertContextTime) {
|
export function setTransactionModelByTransaction(transaction, transaction2, allCategories, allCategoriesMap, allVisibleAccounts, allAccountsMap, allTagsMap, defaultAccountId, options, setContextData, convertContextTime) {
|
||||||
if ((!options.type || options.type === '0') && options.categoryId && options.categoryId !== '0' && allCategoriesMap[options.categoryId]) {
|
if ((!options.type || options.type === '0') && options.categoryId && options.categoryId !== '0' && allCategoriesMap[options.categoryId]) {
|
||||||
const category = allCategoriesMap[options.categoryId];
|
const category = allCategoriesMap[options.categoryId];
|
||||||
const type = categoryTypeToTransactionType(category.type);
|
const type = categoryTypeToTransactionType(category.type);
|
||||||
@@ -105,6 +105,22 @@ export function setTransactionModelByTransaction(transaction, transaction2, allC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (allTagsMap && options.tagIds) {
|
||||||
|
const tagIds = options.tagIds.split(',');
|
||||||
|
const finalTagIds = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < tagIds.length; i++) {
|
||||||
|
const tagId = tagIds[i];
|
||||||
|
const tag = allTagsMap[tagId];
|
||||||
|
|
||||||
|
if (tag && !tag.hidden) {
|
||||||
|
finalTagIds.push(tag.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction.tagIds = finalTagIds;
|
||||||
|
}
|
||||||
|
|
||||||
if (transaction2) {
|
if (transaction2) {
|
||||||
if (setContextData) {
|
if (setContextData) {
|
||||||
transaction.id = transaction2.id;
|
transaction.id = transaction2.id;
|
||||||
|
|||||||
@@ -1025,6 +1025,7 @@ export default {
|
|||||||
'Multiple Accounts': 'Multiple Accounts',
|
'Multiple Accounts': 'Multiple Accounts',
|
||||||
'Source Account': 'Source Account',
|
'Source Account': 'Source Account',
|
||||||
'Destination Account': 'Destination Account',
|
'Destination Account': 'Destination Account',
|
||||||
|
'Multiple Tags': 'Multiple Tags',
|
||||||
'Transaction Time': 'Transaction Time',
|
'Transaction Time': 'Transaction Time',
|
||||||
'Transaction Timezone': 'Transaction Timezone',
|
'Transaction Timezone': 'Transaction Timezone',
|
||||||
'Same time as default timezone': 'Same time as default timezone',
|
'Same time as default timezone': 'Same time as default timezone',
|
||||||
@@ -1098,6 +1099,7 @@ export default {
|
|||||||
'Sort by Name': 'Sort by Name',
|
'Sort by Name': 'Sort by Name',
|
||||||
'Filter Accounts': 'Filter Accounts',
|
'Filter Accounts': 'Filter Accounts',
|
||||||
'Filter Transaction Categories': 'Filter Transaction Categories',
|
'Filter Transaction Categories': 'Filter Transaction Categories',
|
||||||
|
'Filter Transaction Tags': 'Filter Transaction Tags',
|
||||||
'User Settings': 'User Settings',
|
'User Settings': 'User Settings',
|
||||||
'User Profile': 'User Profile',
|
'User Profile': 'User Profile',
|
||||||
'Language': 'Language',
|
'Language': 'Language',
|
||||||
|
|||||||
@@ -1025,6 +1025,7 @@ export default {
|
|||||||
'Multiple Accounts': '多个账户',
|
'Multiple Accounts': '多个账户',
|
||||||
'Source Account': '来源账户',
|
'Source Account': '来源账户',
|
||||||
'Destination Account': '目标账户',
|
'Destination Account': '目标账户',
|
||||||
|
'Multiple Tags': '多个标签',
|
||||||
'Transaction Time': '交易时间',
|
'Transaction Time': '交易时间',
|
||||||
'Transaction Timezone': '交易时区',
|
'Transaction Timezone': '交易时区',
|
||||||
'Same time as default timezone': '与默认时区时间相同',
|
'Same time as default timezone': '与默认时区时间相同',
|
||||||
@@ -1098,6 +1099,7 @@ export default {
|
|||||||
'Sort by Name': '按名称排序',
|
'Sort by Name': '按名称排序',
|
||||||
'Filter Accounts': '过滤账户',
|
'Filter Accounts': '过滤账户',
|
||||||
'Filter Transaction Categories': '过滤交易类型',
|
'Filter Transaction Categories': '过滤交易类型',
|
||||||
|
'Filter Transaction Tags': '过滤交易标签',
|
||||||
'User Settings': '用户设置',
|
'User Settings': '用户设置',
|
||||||
'User Profile': '用户信息',
|
'User Profile': '用户信息',
|
||||||
'Language': '语言',
|
'Language': '语言',
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ const router = createRouter({
|
|||||||
initType: route.query.type,
|
initType: route.query.type,
|
||||||
initCategoryIds: route.query.categoryIds,
|
initCategoryIds: route.query.categoryIds,
|
||||||
initAccountIds: route.query.accountIds,
|
initAccountIds: route.query.accountIds,
|
||||||
|
initTagIds: route.query.tagIds,
|
||||||
initAmountFilter: route.query.amountFilter,
|
initAmountFilter: route.query.amountFilter,
|
||||||
initKeyword: route.query.keyword
|
initKeyword: route.query.keyword
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import TextSizeSettingsPage from '@/views/mobile/settings/TextSizeSettingsPage.v
|
|||||||
import PageSettingsPage from '@/views/mobile/settings/PageSettingsPage.vue';
|
import PageSettingsPage from '@/views/mobile/settings/PageSettingsPage.vue';
|
||||||
import AccountFilterSettingsPage from '@/views/mobile/settings/AccountFilterSettingsPage.vue';
|
import AccountFilterSettingsPage from '@/views/mobile/settings/AccountFilterSettingsPage.vue';
|
||||||
import CategoryFilterSettingsPage from '@/views/mobile/settings/CategoryFilterSettingsPage.vue';
|
import CategoryFilterSettingsPage from '@/views/mobile/settings/CategoryFilterSettingsPage.vue';
|
||||||
|
import TransactionTagFilterSettingsPage from '@/views/mobile/settings/TransactionTagFilterSettingsPage.vue';
|
||||||
|
|
||||||
import SettingsPage from '@/views/mobile/SettingsPage.vue';
|
import SettingsPage from '@/views/mobile/SettingsPage.vue';
|
||||||
import ApplicationLockPage from '@/views/mobile/ApplicationLockPage.vue';
|
import ApplicationLockPage from '@/views/mobile/ApplicationLockPage.vue';
|
||||||
@@ -209,6 +210,11 @@ const routes = [
|
|||||||
async: asyncResolve(CategoryFilterSettingsPage),
|
async: asyncResolve(CategoryFilterSettingsPage),
|
||||||
beforeEnter: [checkLogin]
|
beforeEnter: [checkLogin]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/settings/filter/tag',
|
||||||
|
async: asyncResolve(TransactionTagFilterSettingsPage),
|
||||||
|
beforeEnter: [checkLogin]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/settings/page',
|
path: '/settings/page',
|
||||||
async: asyncResolve(PageSettingsPage),
|
async: asyncResolve(PageSettingsPage),
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ export const useTransactionsStore = defineStore('transactions', {
|
|||||||
type: 0,
|
type: 0,
|
||||||
categoryIds: '',
|
categoryIds: '',
|
||||||
accountIds: '',
|
accountIds: '',
|
||||||
|
tagIds: '',
|
||||||
amountFilter: '',
|
amountFilter: '',
|
||||||
keyword: ''
|
keyword: ''
|
||||||
},
|
},
|
||||||
@@ -324,6 +325,22 @@ export const useTransactionsStore = defineStore('transactions', {
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
|
allFilterTagIds(state) {
|
||||||
|
if (!state.transactionsFilter.tagIds) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const allTagIds = state.transactionsFilter.tagIds.split(',');
|
||||||
|
const ret = {};
|
||||||
|
|
||||||
|
for (let i = 0; i < allTagIds.length; i++) {
|
||||||
|
if (allTagIds[i]) {
|
||||||
|
ret[allTagIds[i]] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
allFilterCategoryIdsCount(state) {
|
allFilterCategoryIdsCount(state) {
|
||||||
if (!state.transactionsFilter.categoryIds) {
|
if (!state.transactionsFilter.categoryIds) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -356,6 +373,22 @@ export const useTransactionsStore = defineStore('transactions', {
|
|||||||
|
|
||||||
return count;
|
return count;
|
||||||
},
|
},
|
||||||
|
allFilterTagIdsCount(state) {
|
||||||
|
if (!state.transactionsFilter.tagIds) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allTagIds = state.transactionsFilter.tagIds.split(',');
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < allTagIds.length; i++) {
|
||||||
|
if (allTagIds[i]) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
},
|
||||||
noTransaction(state) {
|
noTransaction(state) {
|
||||||
for (let i = 0; i < state.transactions.length; i++) {
|
for (let i = 0; i < state.transactions.length; i++) {
|
||||||
const transactionMonthList = state.transactions[i];
|
const transactionMonthList = state.transactions[i];
|
||||||
@@ -444,6 +477,7 @@ export const useTransactionsStore = defineStore('transactions', {
|
|||||||
this.transactionsFilter.type = 0;
|
this.transactionsFilter.type = 0;
|
||||||
this.transactionsFilter.categoryIds = '';
|
this.transactionsFilter.categoryIds = '';
|
||||||
this.transactionsFilter.accountIds = '';
|
this.transactionsFilter.accountIds = '';
|
||||||
|
this.transactionsFilter.tagIds = '';
|
||||||
this.transactionsFilter.amountFilter = '';
|
this.transactionsFilter.amountFilter = '';
|
||||||
this.transactionsFilter.keyword = '';
|
this.transactionsFilter.keyword = '';
|
||||||
this.transactions = [];
|
this.transactions = [];
|
||||||
@@ -492,6 +526,12 @@ export const useTransactionsStore = defineStore('transactions', {
|
|||||||
this.transactionsFilter.accountIds = '';
|
this.transactionsFilter.accountIds = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filter && isString(filter.tagIds)) {
|
||||||
|
this.transactionsFilter.tagIds = filter.tagIds;
|
||||||
|
} else {
|
||||||
|
this.transactionsFilter.tagIds = '';
|
||||||
|
}
|
||||||
|
|
||||||
if (filter && isString(filter.amountFilter)) {
|
if (filter && isString(filter.amountFilter)) {
|
||||||
this.transactionsFilter.amountFilter = filter.amountFilter;
|
this.transactionsFilter.amountFilter = filter.amountFilter;
|
||||||
} else {
|
} else {
|
||||||
@@ -537,6 +577,11 @@ export const useTransactionsStore = defineStore('transactions', {
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filter && isString(filter.tagIds) && this.transactionsFilter.tagIds !== filter.tagIds) {
|
||||||
|
this.transactionsFilter.tagIds = filter.tagIds;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (filter && isString(filter.amountFilter) && this.transactionsFilter.amountFilter !== filter.amountFilter) {
|
if (filter && isString(filter.amountFilter) && this.transactionsFilter.amountFilter !== filter.amountFilter) {
|
||||||
this.transactionsFilter.amountFilter = filter.amountFilter;
|
this.transactionsFilter.amountFilter = filter.amountFilter;
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -564,6 +609,10 @@ export const useTransactionsStore = defineStore('transactions', {
|
|||||||
querys.push('categoryIds=' + this.transactionsFilter.categoryIds);
|
querys.push('categoryIds=' + this.transactionsFilter.categoryIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.transactionsFilter.tagIds) {
|
||||||
|
querys.push('tagIds=' + this.transactionsFilter.tagIds);
|
||||||
|
}
|
||||||
|
|
||||||
querys.push('dateType=' + this.transactionsFilter.dateType);
|
querys.push('dateType=' + this.transactionsFilter.dateType);
|
||||||
|
|
||||||
if (this.transactionsFilter.dateType === datetimeConstants.allDateRanges.Custom.type) {
|
if (this.transactionsFilter.dateType === datetimeConstants.allDateRanges.Custom.type) {
|
||||||
@@ -603,6 +652,7 @@ export const useTransactionsStore = defineStore('transactions', {
|
|||||||
type: self.transactionsFilter.type,
|
type: self.transactionsFilter.type,
|
||||||
categoryIds: self.transactionsFilter.categoryIds,
|
categoryIds: self.transactionsFilter.categoryIds,
|
||||||
accountIds: self.transactionsFilter.accountIds,
|
accountIds: self.transactionsFilter.accountIds,
|
||||||
|
tagIds: self.transactionsFilter.tagIds,
|
||||||
amountFilter: self.transactionsFilter.amountFilter,
|
amountFilter: self.transactionsFilter.amountFilter,
|
||||||
keyword: self.transactionsFilter.keyword
|
keyword: self.transactionsFilter.keyword
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
@@ -678,6 +728,7 @@ export const useTransactionsStore = defineStore('transactions', {
|
|||||||
type: self.transactionsFilter.type,
|
type: self.transactionsFilter.type,
|
||||||
categoryIds: self.transactionsFilter.categoryIds,
|
categoryIds: self.transactionsFilter.categoryIds,
|
||||||
accountIds: self.transactionsFilter.accountIds,
|
accountIds: self.transactionsFilter.accountIds,
|
||||||
|
tagIds: self.transactionsFilter.tagIds,
|
||||||
amountFilter: self.transactionsFilter.amountFilter,
|
amountFilter: self.transactionsFilter.amountFilter,
|
||||||
keyword: self.transactionsFilter.keyword
|
keyword: self.transactionsFilter.keyword
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ export const useTransactionTagsStore = defineStore('transactionTags', {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return allVisibleTags;
|
return allVisibleTags;
|
||||||
|
},
|
||||||
|
allVisibleTagsCount(state) {
|
||||||
|
return state.allTransactionTags.length;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
|||||||
@@ -0,0 +1,284 @@
|
|||||||
|
<template>
|
||||||
|
<v-card :class="{ 'pa-2 pa-sm-4 pa-md-8': dialogMode }">
|
||||||
|
<template #title>
|
||||||
|
<div class="d-flex align-center justify-center" v-if="dialogMode">
|
||||||
|
<div class="w-100 text-center">
|
||||||
|
<h4 class="text-h4">{{ $t(title) }}</h4>
|
||||||
|
</div>
|
||||||
|
<v-btn density="comfortable" color="default" variant="text" class="ml-2"
|
||||||
|
:disabled="loading || !hasAnyAvailableTag" :icon="true">
|
||||||
|
<v-icon :icon="icons.more" />
|
||||||
|
<v-menu activator="parent">
|
||||||
|
<v-list>
|
||||||
|
<v-list-item :prepend-icon="icons.selectAll"
|
||||||
|
:title="$t('Select All')"
|
||||||
|
@click="selectAll"></v-list-item>
|
||||||
|
<v-list-item :prepend-icon="icons.selectNone"
|
||||||
|
:title="$t('Select None')"
|
||||||
|
@click="selectNone"></v-list-item>
|
||||||
|
<v-list-item :prepend-icon="icons.selectInverse"
|
||||||
|
:title="$t('Invert Selection')"
|
||||||
|
@click="selectInvert"></v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-center" v-else-if="!dialogMode">
|
||||||
|
<span>{{ $t(title) }}</span>
|
||||||
|
<v-spacer/>
|
||||||
|
<v-btn density="comfortable" color="default" variant="text" class="ml-2"
|
||||||
|
:disabled="loading" :icon="true">
|
||||||
|
<v-icon :icon="icons.more" />
|
||||||
|
<v-menu activator="parent">
|
||||||
|
<v-list>
|
||||||
|
<v-list-item :prepend-icon="icons.selectAll"
|
||||||
|
:title="$t('Select All')"
|
||||||
|
@click="selectAll"></v-list-item>
|
||||||
|
<v-list-item :prepend-icon="icons.selectNone"
|
||||||
|
:title="$t('Select None')"
|
||||||
|
@click="selectNone"></v-list-item>
|
||||||
|
<v-list-item :prepend-icon="icons.selectInverse"
|
||||||
|
:title="$t('Invert Selection')"
|
||||||
|
@click="selectInvert"></v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div v-if="loading">
|
||||||
|
<v-skeleton-loader type="paragraph" :loading="loading"
|
||||||
|
:key="itemIdx" v-for="itemIdx in [ 1, 2, 3 ]"></v-skeleton-loader>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-card-text :class="{ 'mt-0 mt-sm-2 mt-md-4': dialogMode }" v-if="!loading && !hasAnyAvailableTag">
|
||||||
|
<span class="text-body-1">{{ $t('No available tag') }}</span>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-text :class="{ 'mt-0 mt-sm-2 mt-md-4': dialogMode }" v-else-if="!loading && hasAnyAvailableTag">
|
||||||
|
<v-expansion-panels class="tag-categories" multiple v-model="expandTagCategories">
|
||||||
|
<v-expansion-panel class="border" key="default" value="default">
|
||||||
|
<v-expansion-panel-title class="expand-panel-title-with-bg py-0">
|
||||||
|
<span class="ml-3">{{ $t('Tags') }}</span>
|
||||||
|
</v-expansion-panel-title>
|
||||||
|
<v-expansion-panel-text>
|
||||||
|
<v-list rounded density="comfortable" class="pa-0">
|
||||||
|
<template :key="transactionTag.id"
|
||||||
|
v-for="transactionTag in allVisibleTags">
|
||||||
|
<v-list-item>
|
||||||
|
<template #prepend>
|
||||||
|
<v-checkbox :model-value="!filterTagIds[transactionTag.id]"
|
||||||
|
@update:model-value="selectTransactionTag(transactionTag, $event)">
|
||||||
|
<template #label>
|
||||||
|
<v-icon size="24" :icon="icons.tag"/>
|
||||||
|
<span class="ml-3">{{ transactionTag.name }}</span>
|
||||||
|
</template>
|
||||||
|
</v-checkbox>
|
||||||
|
</template>
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
|
</v-list>
|
||||||
|
</v-expansion-panel-text>
|
||||||
|
</v-expansion-panel>
|
||||||
|
</v-expansion-panels>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-text class="overflow-y-visible" v-if="dialogMode">
|
||||||
|
<div class="w-100 d-flex justify-center mt-2 mt-sm-4 mt-md-6 gap-4">
|
||||||
|
<v-btn :disabled="!hasAnyAvailableTag" @click="save">{{ $t(applyText) }}</v-btn>
|
||||||
|
<v-btn color="secondary" variant="tonal" @click="cancel">{{ $t('Cancel') }}</v-btn>
|
||||||
|
</div>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapStores } from 'pinia';
|
||||||
|
import { useTransactionTagsStore } from '@/stores/transactionTag.js';
|
||||||
|
import { useTransactionsStore } from '@/stores/transaction.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
selectAll,
|
||||||
|
selectNone,
|
||||||
|
selectInvert
|
||||||
|
} from '@/lib/common.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
mdiSelectAll,
|
||||||
|
mdiSelect,
|
||||||
|
mdiSelectInverse,
|
||||||
|
mdiDotsVertical,
|
||||||
|
mdiPound
|
||||||
|
} from '@mdi/js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: [
|
||||||
|
'dialogMode',
|
||||||
|
'type',
|
||||||
|
'autoSave'
|
||||||
|
],
|
||||||
|
emits: [
|
||||||
|
'settings:change'
|
||||||
|
],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
expandTagCategories: [ 'default' ],
|
||||||
|
filterTagIds: {},
|
||||||
|
icons: {
|
||||||
|
selectAll: mdiSelectAll,
|
||||||
|
selectNone: mdiSelect,
|
||||||
|
selectInverse: mdiSelectInverse,
|
||||||
|
more: mdiDotsVertical,
|
||||||
|
tag: mdiPound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapStores(useTransactionTagsStore, useTransactionsStore),
|
||||||
|
title() {
|
||||||
|
return 'Filter Transaction Tags';
|
||||||
|
},
|
||||||
|
applyText() {
|
||||||
|
return 'Apply';
|
||||||
|
},
|
||||||
|
allVisibleTags() {
|
||||||
|
return this.transactionTagsStore.allVisibleTags;
|
||||||
|
},
|
||||||
|
hasAnyAvailableTag() {
|
||||||
|
return this.transactionTagsStore.allVisibleTagsCount > 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
self.transactionTagsStore.loadAllTags({
|
||||||
|
force: false
|
||||||
|
}).then(() => {
|
||||||
|
self.loading = false;
|
||||||
|
|
||||||
|
const allTransactionTagIds = {};
|
||||||
|
|
||||||
|
for (let transactionTagId in self.transactionTagsStore.allTransactionTagsMap) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(self.transactionTagsStore.allTransactionTagsMap, transactionTagId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transactionTag = self.transactionTagsStore.allTransactionTagsMap[transactionTagId];
|
||||||
|
|
||||||
|
if (self.type === 'transactionListCurrent' && self.transactionsStore.allFilterTagIdsCount > 0) {
|
||||||
|
allTransactionTagIds[transactionTag.id] = true;
|
||||||
|
} else {
|
||||||
|
allTransactionTagIds[transactionTag.id] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.type === 'transactionListCurrent') {
|
||||||
|
for (let transactionTagId in self.transactionsStore.allFilterTagIds) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(self.transactionsStore.allFilterTagIds, transactionTagId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transactionTag = self.transactionTagsStore.allTransactionTagsMap[transactionTagId];
|
||||||
|
|
||||||
|
if (transactionTag) {
|
||||||
|
allTransactionTagIds[transactionTag.id] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.filterTagIds = allTransactionTagIds;
|
||||||
|
} else {
|
||||||
|
self.$refs.snackbar.showError('Parameter Invalid');
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
self.loading = false;
|
||||||
|
|
||||||
|
if (!error.processed) {
|
||||||
|
self.$refs.snackbar.showError(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
save() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
const filteredTagIds = {};
|
||||||
|
let isAllSelected = true;
|
||||||
|
let finalTagIds = '';
|
||||||
|
let changed = true;
|
||||||
|
|
||||||
|
for (let transactionTagId in self.filterTagIds) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(self.filterTagIds, transactionTagId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transactionTag = self.transactionTagsStore.allTransactionTagsMap[transactionTagId];
|
||||||
|
|
||||||
|
if (self.filterTagIds[transactionTag.id]) {
|
||||||
|
filteredTagIds[transactionTag.id] = true;
|
||||||
|
isAllSelected = false;
|
||||||
|
} else {
|
||||||
|
if (finalTagIds.length > 0) {
|
||||||
|
finalTagIds += ',';
|
||||||
|
}
|
||||||
|
|
||||||
|
finalTagIds += transactionTag.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.type === 'transactionListCurrent') {
|
||||||
|
changed = self.transactionsStore.updateTransactionListFilter({
|
||||||
|
tagIds: isAllSelected ? '' : finalTagIds
|
||||||
|
});
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
self.transactionsStore.updateTransactionListInvalidState(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.$emit('settings:change', changed);
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
this.$emit('settings:change', false);
|
||||||
|
},
|
||||||
|
selectTransactionTag(transactionTag, value) {
|
||||||
|
this.filterTagIds[transactionTag.id] = !value;
|
||||||
|
|
||||||
|
if (this.autoSave) {
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectAll() {
|
||||||
|
selectAll(this.filterTagIds, this.transactionTagsStore.allTransactionTagsMap);
|
||||||
|
|
||||||
|
if (this.autoSave) {
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectNone() {
|
||||||
|
selectNone(this.filterTagIds, this.transactionTagsStore.allTransactionTagsMap);
|
||||||
|
|
||||||
|
if (this.autoSave) {
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectInvert() {
|
||||||
|
selectInvert(this.filterTagIds, this.transactionTagsStore.allTransactionTagsMap);
|
||||||
|
|
||||||
|
if (this.autoSave) {
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.tag-categories .v-expansion-panel-text__wrapper {
|
||||||
|
padding: 0 0 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-categories .v-expansion-panel--active:not(:first-child),
|
||||||
|
.tag-categories .v-expansion-panel--active + .v-expansion-panel {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -308,7 +308,61 @@
|
|||||||
</v-list>
|
</v-list>
|
||||||
</v-menu>
|
</v-menu>
|
||||||
</th>
|
</th>
|
||||||
<th class="transaction-table-column-tags" v-if="showTagInTransactionListPage">{{ $t('Tags') }}</th>
|
<th class="transaction-table-column-tags" v-if="showTagInTransactionListPage">
|
||||||
|
<v-menu ref="tagFilterMenu" class="transaction-tag-menu"
|
||||||
|
eager location="bottom" max-height="500"
|
||||||
|
@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">
|
||||||
|
<span>{{ queryTagName }}</span>
|
||||||
|
<v-icon :icon="icons.dropdownMenu" />
|
||||||
|
</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 ? icons.check : null)">
|
||||||
|
<v-list-item-title class="cursor-pointer"
|
||||||
|
@click="changeTagFilter('')">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<v-icon :icon="icons.all" />
|
||||||
|
<span class="text-sm ml-3">{{ $t('All') }}</span>
|
||||||
|
</div>
|
||||||
|
</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 ? icons.check : null)"
|
||||||
|
v-if="allVisibleTagsCount > 0">
|
||||||
|
<v-list-item-title class="cursor-pointer"
|
||||||
|
@click="showFilterTagDialog = true">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<v-icon :icon="icons.multiple" />
|
||||||
|
<span class="text-sm ml-3">{{ $t('Multiple Tags') }}</span>
|
||||||
|
</div>
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
<template :key="transactionTag.id"
|
||||||
|
v-for="transactionTag in allTransactionTags">
|
||||||
|
<v-divider v-if="!transactionTag.hidden" />
|
||||||
|
<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 ? icons.check : null)"
|
||||||
|
v-if="!transactionTag.hidden">
|
||||||
|
<v-list-item-title class="cursor-pointer"
|
||||||
|
@click="changeTagFilter(transactionTag.id)">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<v-icon size="24" :icon="icons.tag"/>
|
||||||
|
<span class="text-sm ml-3">{{ transactionTag.name }}</span>
|
||||||
|
</div>
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</th>
|
||||||
<th class="transaction-table-column-description">{{ $t('Description') }}</th>
|
<th class="transaction-table-column-description">{{ $t('Description') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -427,6 +481,11 @@
|
|||||||
@settings:change="changeMultipleCategoriesFilter" />
|
@settings:change="changeMultipleCategoriesFilter" />
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
|
|
||||||
|
<v-dialog width="800" v-model="showFilterTagDialog">
|
||||||
|
<transaction-tag-filter-settings-card type="transactionListCurrent" :dialog-mode="true"
|
||||||
|
@settings:change="changeMultipleTagsFilter" />
|
||||||
|
</v-dialog>
|
||||||
|
|
||||||
<confirm-dialog ref="confirmDialog"/>
|
<confirm-dialog ref="confirmDialog"/>
|
||||||
<snack-bar ref="snackbar" />
|
<snack-bar ref="snackbar" />
|
||||||
</template>
|
</template>
|
||||||
@@ -435,6 +494,7 @@
|
|||||||
import EditDialog from './list/dialogs/EditDialog.vue';
|
import EditDialog from './list/dialogs/EditDialog.vue';
|
||||||
import AccountFilterSettingsCard from '@/views/desktop/common/cards/AccountFilterSettingsCard.vue';
|
import AccountFilterSettingsCard from '@/views/desktop/common/cards/AccountFilterSettingsCard.vue';
|
||||||
import CategoryFilterSettingsCard from '@/views/desktop/common/cards/CategoryFilterSettingsCard.vue';
|
import CategoryFilterSettingsCard from '@/views/desktop/common/cards/CategoryFilterSettingsCard.vue';
|
||||||
|
import TransactionTagFilterSettingsCard from '@/views/desktop/common/cards/TransactionTagFilterSettingsCard.vue';
|
||||||
|
|
||||||
import { useDisplay } from 'vuetify';
|
import { useDisplay } from 'vuetify';
|
||||||
|
|
||||||
@@ -495,6 +555,7 @@ import {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
TransactionTagFilterSettingsCard,
|
||||||
EditDialog,
|
EditDialog,
|
||||||
AccountFilterSettingsCard,
|
AccountFilterSettingsCard,
|
||||||
CategoryFilterSettingsCard
|
CategoryFilterSettingsCard
|
||||||
@@ -506,6 +567,7 @@ export default {
|
|||||||
'initType',
|
'initType',
|
||||||
'initCategoryIds',
|
'initCategoryIds',
|
||||||
'initAccountIds',
|
'initAccountIds',
|
||||||
|
'initTagIds',
|
||||||
'initAmountFilter',
|
'initAmountFilter',
|
||||||
'initKeyword'
|
'initKeyword'
|
||||||
],
|
],
|
||||||
@@ -533,6 +595,7 @@ export default {
|
|||||||
showCustomDateRangeDialog: false,
|
showCustomDateRangeDialog: false,
|
||||||
showFilterAccountDialog: false,
|
showFilterAccountDialog: false,
|
||||||
showFilterCategoryDialog: false,
|
showFilterCategoryDialog: false,
|
||||||
|
showFilterTagDialog: false,
|
||||||
icons: {
|
icons: {
|
||||||
search: mdiMagnify,
|
search: mdiMagnify,
|
||||||
check: mdiCheck,
|
check: mdiCheck,
|
||||||
@@ -606,12 +669,18 @@ export default {
|
|||||||
queryAllFilterAccountIds() {
|
queryAllFilterAccountIds() {
|
||||||
return this.transactionsStore.allFilterAccountIds;
|
return this.transactionsStore.allFilterAccountIds;
|
||||||
},
|
},
|
||||||
|
queryAllFilterTagIds() {
|
||||||
|
return this.transactionsStore.allFilterTagIds;
|
||||||
|
},
|
||||||
queryAllFilterCategoryIdsCount() {
|
queryAllFilterCategoryIdsCount() {
|
||||||
return this.transactionsStore.allFilterCategoryIdsCount;
|
return this.transactionsStore.allFilterCategoryIdsCount;
|
||||||
},
|
},
|
||||||
queryAllFilterAccountIdsCount() {
|
queryAllFilterAccountIdsCount() {
|
||||||
return this.transactionsStore.allFilterAccountIdsCount;
|
return this.transactionsStore.allFilterAccountIdsCount;
|
||||||
},
|
},
|
||||||
|
queryAllFilterTagIdsCount() {
|
||||||
|
return this.transactionsStore.allFilterTagIdsCount;
|
||||||
|
},
|
||||||
queryAllSelectedFilterCategoryIds() {
|
queryAllSelectedFilterCategoryIds() {
|
||||||
if (this.queryAllFilterCategoryIdsCount === 0) {
|
if (this.queryAllFilterCategoryIdsCount === 0) {
|
||||||
return '';
|
return '';
|
||||||
@@ -630,6 +699,15 @@ export default {
|
|||||||
return 'multiple';
|
return 'multiple';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
queryAllSelectedFilterTagIds() {
|
||||||
|
if (this.queryAllFilterTagIdsCount === 0) {
|
||||||
|
return '';
|
||||||
|
} else if (this.queryAllFilterTagIdsCount === 1) {
|
||||||
|
return this.query.tagIds;
|
||||||
|
} else { // this.queryAllFilterTagIdsCount > 1
|
||||||
|
return 'multiple';
|
||||||
|
}
|
||||||
|
},
|
||||||
queryCategoryName() {
|
queryCategoryName() {
|
||||||
if (this.queryAllFilterCategoryIdsCount > 1) {
|
if (this.queryAllFilterCategoryIdsCount > 1) {
|
||||||
return this.$t('Multiple Categories');
|
return this.$t('Multiple Categories');
|
||||||
@@ -644,6 +722,13 @@ export default {
|
|||||||
|
|
||||||
return getNameByKeyValue(this.allAccounts, this.query.accountIds, null, 'name', this.$t('Account'));
|
return getNameByKeyValue(this.allAccounts, this.query.accountIds, null, 'name', this.$t('Account'));
|
||||||
},
|
},
|
||||||
|
queryTagName() {
|
||||||
|
if (this.queryAllFilterTagIdsCount > 1) {
|
||||||
|
return this.$t('Multiple Tags');
|
||||||
|
}
|
||||||
|
|
||||||
|
return getNameByKeyValue(this.allTransactionTags, this.query.tagIds, null, 'name', this.$t('Tags'));
|
||||||
|
},
|
||||||
queryAmount() {
|
queryAmount() {
|
||||||
if (!this.query.amountFilter) {
|
if (!this.query.amountFilter) {
|
||||||
return '';
|
return '';
|
||||||
@@ -802,6 +887,9 @@ export default {
|
|||||||
allTransactionTags() {
|
allTransactionTags() {
|
||||||
return this.transactionTagsStore.allTransactionTagsMap;
|
return this.transactionTagsStore.allTransactionTagsMap;
|
||||||
},
|
},
|
||||||
|
allVisibleTagsCount() {
|
||||||
|
return this.transactionTagsStore.allVisibleTagsCount;
|
||||||
|
},
|
||||||
recentMonthDateRanges() {
|
recentMonthDateRanges() {
|
||||||
return this.$locale.getAllRecentMonthDateRanges(this.userStore, true, true);
|
return this.$locale.getAllRecentMonthDateRanges(this.userStore, true, true);
|
||||||
},
|
},
|
||||||
@@ -820,6 +908,7 @@ export default {
|
|||||||
type: this.initType,
|
type: this.initType,
|
||||||
categoryIds: this.initCategoryIds,
|
categoryIds: this.initCategoryIds,
|
||||||
accountIds: this.initAccountIds,
|
accountIds: this.initAccountIds,
|
||||||
|
tagIds: this.initTagIds,
|
||||||
amountFilter: this.initAmountFilter,
|
amountFilter: this.initAmountFilter,
|
||||||
keyword: this.initKeyword
|
keyword: this.initKeyword
|
||||||
});
|
});
|
||||||
@@ -849,6 +938,7 @@ export default {
|
|||||||
type: to.query.type,
|
type: to.query.type,
|
||||||
categoryIds: to.query.categoryIds,
|
categoryIds: to.query.categoryIds,
|
||||||
accountIds: to.query.accountIds,
|
accountIds: to.query.accountIds,
|
||||||
|
tagIds: to.query.tagIds,
|
||||||
amountFilter: to.query.amountFilter,
|
amountFilter: to.query.amountFilter,
|
||||||
keyword: to.query.keyword
|
keyword: to.query.keyword
|
||||||
});
|
});
|
||||||
@@ -875,6 +965,7 @@ export default {
|
|||||||
type: parseInt(query.type) > 0 ? parseInt(query.type) : undefined,
|
type: parseInt(query.type) > 0 ? parseInt(query.type) : undefined,
|
||||||
categoryIds: query.categoryIds,
|
categoryIds: query.categoryIds,
|
||||||
accountIds: query.accountIds,
|
accountIds: query.accountIds,
|
||||||
|
tagIds: query.tagIds,
|
||||||
amountFilter: query.amountFilter || '',
|
amountFilter: query.amountFilter || '',
|
||||||
keyword: query.keyword || ''
|
keyword: query.keyword || ''
|
||||||
});
|
});
|
||||||
@@ -1156,6 +1247,32 @@ export default {
|
|||||||
this.$router.push(this.getFilterLinkUrl());
|
this.$router.push(this.getFilterLinkUrl());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
changeTagFilter(tagIds) {
|
||||||
|
if (this.query.tagIds === tagIds) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const changed = this.transactionsStore.updateTransactionListFilter({
|
||||||
|
tagIds: tagIds
|
||||||
|
});
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
this.loading = true;
|
||||||
|
this.currentPageTransactions = [];
|
||||||
|
this.transactionsStore.clearTransactions();
|
||||||
|
this.$router.push(this.getFilterLinkUrl());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
changeMultipleTagsFilter(changed) {
|
||||||
|
this.showFilterTagDialog = false;
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
this.loading = true;
|
||||||
|
this.currentPageTransactions = [];
|
||||||
|
this.transactionsStore.clearTransactions();
|
||||||
|
this.$router.push(this.getFilterLinkUrl());
|
||||||
|
}
|
||||||
|
},
|
||||||
changeKeywordFilter(keyword) {
|
changeKeywordFilter(keyword) {
|
||||||
if (this.query.keyword === keyword) {
|
if (this.query.keyword === keyword) {
|
||||||
return;
|
return;
|
||||||
@@ -1179,7 +1296,8 @@ export default {
|
|||||||
self.$refs.editDialog.open({
|
self.$refs.editDialog.open({
|
||||||
type: self.query.type,
|
type: self.query.type,
|
||||||
categoryId: self.queryAllFilterCategoryIdsCount === 1 ? self.query.categoryIds : '',
|
categoryId: self.queryAllFilterCategoryIdsCount === 1 ? self.query.categoryIds : '',
|
||||||
accountId: self.queryAllFilterAccountIdsCount === 1 ? self.query.accountIds : ''
|
accountId: self.queryAllFilterAccountIdsCount === 1 ? self.query.accountIds : '',
|
||||||
|
tagIds: self.query.tagIds || ''
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
if (result && result.message) {
|
if (result && result.message) {
|
||||||
self.$refs.snackbar.showMessage(result.message);
|
self.$refs.snackbar.showMessage(result.message);
|
||||||
@@ -1252,6 +1370,11 @@ export default {
|
|||||||
this.scrollMenuToSelectedItem(this.$refs.accountFilterMenu);
|
this.scrollMenuToSelectedItem(this.$refs.accountFilterMenu);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
scrollTagMenuToSelectedItem(opened) {
|
||||||
|
if (opened) {
|
||||||
|
this.scrollMenuToSelectedItem(this.$refs.tagFilterMenu);
|
||||||
|
}
|
||||||
|
},
|
||||||
scrollMenuToSelectedItem(menu) {
|
scrollMenuToSelectedItem(menu) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
scrollToSelectedItem(menu.contentEl, 'div.v-list', 'div.v-list-item.list-item-selected');
|
scrollToSelectedItem(menu.contentEl, 'div.v-list', 'div.v-list-item.list-item-selected');
|
||||||
@@ -1375,7 +1498,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.transaction-table .transaction-table-column-tags {
|
.transaction-table .transaction-table-column-tags {
|
||||||
width: 80px;
|
width: 90px;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1409,6 +1532,7 @@ export default {
|
|||||||
.transaction-category-menu .item-icon,
|
.transaction-category-menu .item-icon,
|
||||||
.transaction-amount-menu .item-icon,
|
.transaction-amount-menu .item-icon,
|
||||||
.transaction-account-menu .item-icon,
|
.transaction-account-menu .item-icon,
|
||||||
|
.transaction-tag-menu .item-icon,
|
||||||
.transaction-table .item-icon {
|
.transaction-table .item-icon {
|
||||||
padding-bottom: 3px;
|
padding-bottom: 3px;
|
||||||
}
|
}
|
||||||
@@ -1424,7 +1548,8 @@ export default {
|
|||||||
|
|
||||||
.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-category-menu .item-in-multiple-selection span,
|
||||||
.transaction-account-menu .item-in-multiple-selection span {
|
.transaction-account-menu .item-in-multiple-selection span,
|
||||||
|
.transaction-tag-menu .item-in-multiple-selection span {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -488,6 +488,9 @@ export default {
|
|||||||
allTags() {
|
allTags() {
|
||||||
return this.transactionTagsStore.allVisibleTags;
|
return this.transactionTagsStore.allVisibleTags;
|
||||||
},
|
},
|
||||||
|
allTagsMap() {
|
||||||
|
return this.transactionTagsStore.allTransactionTagsMap;
|
||||||
|
},
|
||||||
hasAvailableExpenseCategories() {
|
hasAvailableExpenseCategories() {
|
||||||
if (!this.allCategories || !this.allCategories[this.allCategoryTypes.Expense] || !this.allCategories[this.allCategoryTypes.Expense].length) {
|
if (!this.allCategories || !this.allCategories[this.allCategoryTypes.Expense] || !this.allCategories[this.allCategoryTypes.Expense].length) {
|
||||||
return false;
|
return false;
|
||||||
@@ -847,11 +850,13 @@ export default {
|
|||||||
this.allCategoriesMap,
|
this.allCategoriesMap,
|
||||||
this.allVisibleAccounts,
|
this.allVisibleAccounts,
|
||||||
this.allAccountsMap,
|
this.allAccountsMap,
|
||||||
|
this.allTagsMap,
|
||||||
this.defaultAccountId,
|
this.defaultAccountId,
|
||||||
{
|
{
|
||||||
type: options.type,
|
type: options.type,
|
||||||
categoryId: options.categoryId,
|
categoryId: options.categoryId,
|
||||||
accountId: options.accountId
|
accountId: options.accountId,
|
||||||
|
tagIds: options.tagIds
|
||||||
},
|
},
|
||||||
setContextData,
|
setContextData,
|
||||||
convertContextTime
|
convertContextTime
|
||||||
|
|||||||
@@ -0,0 +1,255 @@
|
|||||||
|
<template>
|
||||||
|
<f7-page @page:afterin="onPageAfterIn">
|
||||||
|
<f7-navbar>
|
||||||
|
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
|
||||||
|
<f7-nav-title :title="$t(title)"></f7-nav-title>
|
||||||
|
<f7-nav-right>
|
||||||
|
<f7-link icon-f7="ellipsis" :class="{ 'disabled': !hasAnyAvailableTag }" @click="showMoreActionSheet = true"></f7-link>
|
||||||
|
<f7-link :text="$t(applyText)" :class="{ 'disabled': !hasAnyAvailableTag }" @click="save"></f7-link>
|
||||||
|
</f7-nav-right>
|
||||||
|
</f7-navbar>
|
||||||
|
|
||||||
|
<f7-block class="combination-list-wrapper margin-vertical skeleton-text" v-if="loading">
|
||||||
|
<f7-accordion-item>
|
||||||
|
<f7-block-title>
|
||||||
|
<f7-accordion-toggle>
|
||||||
|
<f7-list strong inset dividers media-list
|
||||||
|
class="combination-list-header combination-list-opened">
|
||||||
|
<f7-list-item>
|
||||||
|
<template #title>
|
||||||
|
<span>Tags</span>
|
||||||
|
<f7-icon class="combination-list-chevron-icon" f7="chevron_up"></f7-icon>
|
||||||
|
</template>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
</f7-accordion-toggle>
|
||||||
|
</f7-block-title>
|
||||||
|
<f7-accordion-content style="height: auto">
|
||||||
|
<f7-list strong inset dividers accordion-list class="combination-list-content">
|
||||||
|
<f7-list-item checkbox class="disabled" title="Tag Name"
|
||||||
|
:key="itemIdx" v-for="itemIdx in [ 1, 2, 3 ]">
|
||||||
|
<template #media>
|
||||||
|
<f7-icon f7="app_fill"></f7-icon>
|
||||||
|
</template>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
</f7-accordion-content>
|
||||||
|
</f7-accordion-item>
|
||||||
|
</f7-block>
|
||||||
|
|
||||||
|
<f7-list strong inset dividers accordion-list class="margin-top" v-if="!loading && !hasAnyAvailableTag">
|
||||||
|
<f7-list-item :title="$t('No available tag')"></f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
|
||||||
|
<f7-block class="combination-list-wrapper margin-vertical" key="default" v-if="!loading">
|
||||||
|
<f7-accordion-item :opened="collapseStates['default'].opened"
|
||||||
|
@accordion:open="collapseStates['default'].opened = true"
|
||||||
|
@accordion:close="collapseStates['default'].opened = false">
|
||||||
|
<f7-block-title>
|
||||||
|
<f7-accordion-toggle>
|
||||||
|
<f7-list strong inset dividers media-list
|
||||||
|
class="combination-list-header"
|
||||||
|
:class="collapseStates['default'].opened ? 'combination-list-opened' : 'combination-list-closed'">
|
||||||
|
<f7-list-item>
|
||||||
|
<template #title>
|
||||||
|
<span>{{ $t('Tags') }}</span>
|
||||||
|
<f7-icon class="combination-list-chevron-icon" :f7="collapseStates['default'].opened ? 'chevron_up' : 'chevron_down'"></f7-icon>
|
||||||
|
</template>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
</f7-accordion-toggle>
|
||||||
|
</f7-block-title>
|
||||||
|
<f7-accordion-content :style="{ height: collapseStates['default'].opened ? 'auto' : '' }">
|
||||||
|
<f7-list strong inset dividers accordion-list class="combination-list-content">
|
||||||
|
<f7-list-item checkbox
|
||||||
|
:title="transactionTag.name"
|
||||||
|
:value="transactionTag.id"
|
||||||
|
:checked="!filterTagIds[transactionTag.id]"
|
||||||
|
:key="transactionTag.id"
|
||||||
|
v-for="transactionTag in allVisibleTags"
|
||||||
|
@change="selectTransactionTag">
|
||||||
|
<template #media>
|
||||||
|
<f7-icon f7="number"></f7-icon>
|
||||||
|
</template>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
</f7-accordion-content>
|
||||||
|
</f7-accordion-item>
|
||||||
|
</f7-block>
|
||||||
|
|
||||||
|
<f7-actions close-by-outside-click close-on-escape :opened="showMoreActionSheet" @actions:closed="showMoreActionSheet = false">
|
||||||
|
<f7-actions-group>
|
||||||
|
<f7-actions-button @click="selectAll">{{ $t('Select All') }}</f7-actions-button>
|
||||||
|
<f7-actions-button @click="selectNone">{{ $t('Select None') }}</f7-actions-button>
|
||||||
|
<f7-actions-button @click="selectInvert">{{ $t('Invert Selection') }}</f7-actions-button>
|
||||||
|
</f7-actions-group>
|
||||||
|
<f7-actions-group>
|
||||||
|
<f7-actions-button bold close>{{ $t('Cancel') }}</f7-actions-button>
|
||||||
|
</f7-actions-group>
|
||||||
|
</f7-actions>
|
||||||
|
</f7-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapStores } from 'pinia';
|
||||||
|
import { useTransactionTagsStore } from '@/stores/transactionTag.js';
|
||||||
|
import { useTransactionsStore } from '@/stores/transaction.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
selectAll,
|
||||||
|
selectNone,
|
||||||
|
selectInvert
|
||||||
|
} from '@/lib/common.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: [
|
||||||
|
'f7route',
|
||||||
|
'f7router'
|
||||||
|
],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
loadingError: null,
|
||||||
|
type: null,
|
||||||
|
filterTagIds: {},
|
||||||
|
collapseStates: {
|
||||||
|
'default': {
|
||||||
|
opened: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showMoreActionSheet: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapStores(useTransactionTagsStore, useTransactionsStore),
|
||||||
|
title() {
|
||||||
|
return 'Filter Transaction Tags';
|
||||||
|
},
|
||||||
|
applyText() {
|
||||||
|
return 'Apply';
|
||||||
|
},
|
||||||
|
allVisibleTags() {
|
||||||
|
return this.transactionTagsStore.allVisibleTags;
|
||||||
|
},
|
||||||
|
hasAnyAvailableTag() {
|
||||||
|
return this.transactionTagsStore.allVisibleTagsCount > 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
const self = this;
|
||||||
|
const query = self.f7route.query;
|
||||||
|
|
||||||
|
self.type = query.type;
|
||||||
|
|
||||||
|
self.transactionTagsStore.loadAllTags({
|
||||||
|
force: false
|
||||||
|
}).then(() => {
|
||||||
|
self.loading = false;
|
||||||
|
|
||||||
|
const allTransactionTagIds = {};
|
||||||
|
|
||||||
|
for (let transactionTagId in self.transactionTagsStore.allTransactionTagsMap) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(self.transactionTagsStore.allTransactionTagsMap, transactionTagId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transactionTag = self.transactionTagsStore.allTransactionTagsMap[transactionTagId];
|
||||||
|
|
||||||
|
if (self.type === 'transactionListCurrent' && self.transactionsStore.allFilterTagIdsCount > 0) {
|
||||||
|
allTransactionTagIds[transactionTag.id] = true;
|
||||||
|
} else {
|
||||||
|
allTransactionTagIds[transactionTag.id] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.type === 'transactionListCurrent') {
|
||||||
|
for (let transactionTagId in self.transactionsStore.allFilterTagIds) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(self.transactionsStore.allFilterTagIds, transactionTagId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transactionTag = self.transactionTagsStore.allTransactionTagsMap[transactionTagId];
|
||||||
|
|
||||||
|
if (transactionTag) {
|
||||||
|
allTransactionTagIds[transactionTag.id] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.filterTagIds = allTransactionTagIds;
|
||||||
|
} else {
|
||||||
|
self.$toast('Parameter Invalid');
|
||||||
|
self.loadingError = 'Parameter Invalid';
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
if (error.processed) {
|
||||||
|
self.loading = false;
|
||||||
|
} else {
|
||||||
|
self.loadingError = error;
|
||||||
|
self.$toast(error.message || error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onPageAfterIn() {
|
||||||
|
this.$routeBackOnError(this.f7router, 'loadingError');
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
const self = this;
|
||||||
|
const router = self.f7router;
|
||||||
|
|
||||||
|
const filteredTagIds = {};
|
||||||
|
let isAllSelected = true;
|
||||||
|
let finalTagIds = '';
|
||||||
|
|
||||||
|
for (let transactionTagId in self.filterTagIds) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(self.filterTagIds, transactionTagId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transactionTag = self.transactionTagsStore.allTransactionTagsMap[transactionTagId];
|
||||||
|
|
||||||
|
if (self.filterTagIds[transactionTag.id]) {
|
||||||
|
filteredTagIds[transactionTag.id] = true;
|
||||||
|
isAllSelected = false;
|
||||||
|
} else {
|
||||||
|
if (finalTagIds.length > 0) {
|
||||||
|
finalTagIds += ',';
|
||||||
|
}
|
||||||
|
|
||||||
|
finalTagIds += transactionTag.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.type === 'transactionListCurrent') {
|
||||||
|
const changed = self.transactionsStore.updateTransactionListFilter({
|
||||||
|
tagIds: isAllSelected ? '' : finalTagIds
|
||||||
|
});
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
self.transactionsStore.updateTransactionListInvalidState(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
router.back();
|
||||||
|
},
|
||||||
|
selectTransactionTag(e) {
|
||||||
|
const transactionTagId = e.target.value;
|
||||||
|
const transactionTag = this.transactionTagsStore.allTransactionTagsMap[transactionTagId];
|
||||||
|
|
||||||
|
if (!transactionTag) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.filterTagIds[transactionTag.id] = !e.target.checked;
|
||||||
|
},
|
||||||
|
selectAll() {
|
||||||
|
selectAll(this.filterTagIds, this.transactionTagsStore.allTransactionTagsMap);
|
||||||
|
},
|
||||||
|
selectNone() {
|
||||||
|
selectNone(this.filterTagIds, this.transactionTagsStore.allTransactionTagsMap);
|
||||||
|
},
|
||||||
|
selectInvert() {
|
||||||
|
selectInvert(this.filterTagIds, this.transactionTagsStore.allTransactionTagsMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -504,6 +504,9 @@ export default {
|
|||||||
allTags() {
|
allTags() {
|
||||||
return this.transactionTagsStore.allTransactionTags;
|
return this.transactionTagsStore.allTransactionTags;
|
||||||
},
|
},
|
||||||
|
allTagsMap() {
|
||||||
|
return this.transactionTagsStore.allTransactionTagsMap;
|
||||||
|
},
|
||||||
hasAvailableExpenseCategories() {
|
hasAvailableExpenseCategories() {
|
||||||
if (!this.allCategories || !this.allCategories[this.allCategoryTypes.Expense] || !this.allCategories[this.allCategoryTypes.Expense].length) {
|
if (!this.allCategories || !this.allCategories[this.allCategoryTypes.Expense] || !this.allCategories[this.allCategoryTypes.Expense].length) {
|
||||||
return false;
|
return false;
|
||||||
@@ -683,11 +686,13 @@ export default {
|
|||||||
self.allCategoriesMap,
|
self.allCategoriesMap,
|
||||||
self.allVisibleAccounts,
|
self.allVisibleAccounts,
|
||||||
self.allAccountsMap,
|
self.allAccountsMap,
|
||||||
|
self.allTagsMap,
|
||||||
self.defaultAccountId,
|
self.defaultAccountId,
|
||||||
{
|
{
|
||||||
type: query.type,
|
type: query.type,
|
||||||
categoryId: query.categoryId,
|
categoryId: query.categoryId,
|
||||||
accountId: query.accountId
|
accountId: query.accountId,
|
||||||
|
tagIds: query.tagIds
|
||||||
},
|
},
|
||||||
(self.mode === 'edit' || self.mode === 'view'),
|
(self.mode === 'edit' || self.mode === 'view'),
|
||||||
(self.mode === 'edit' || self.mode === 'view')
|
(self.mode === 'edit' || self.mode === 'view')
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
|
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
|
||||||
<f7-nav-title :title="$t('Transaction List')"></f7-nav-title>
|
<f7-nav-title :title="$t('Transaction List')"></f7-nav-title>
|
||||||
<f7-nav-right class="navbar-compact-icons">
|
<f7-nav-right class="navbar-compact-icons">
|
||||||
<f7-link icon-f7="plus" :class="{ 'disabled': !canAddTransaction }" :href="`/transaction/add?type=${query.type}&categoryId=${queryAllFilterCategoryIdsCount === 1 ? query.categoryIds : ''}&accountId=${queryAllFilterAccountIdsCount === 1 ? query.accountIds : ''}`"></f7-link>
|
<f7-link icon-f7="plus" :class="{ 'disabled': !canAddTransaction }" :href="`/transaction/add?type=${query.type}&categoryId=${queryAllFilterCategoryIdsCount === 1 ? query.categoryIds : ''}&accountId=${queryAllFilterAccountIdsCount === 1 ? query.accountIds : ''}&tagIds=${query.tagIds || ''}`"></f7-link>
|
||||||
</f7-nav-right>
|
</f7-nav-right>
|
||||||
|
|
||||||
<f7-subnavbar :inner="false">
|
<f7-subnavbar :inner="false">
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<span :class="{ 'tabbar-item-changed': query.accountIds }">{{ queryAccountName }}</span>
|
<span :class="{ 'tabbar-item-changed': query.accountIds }">{{ queryAccountName }}</span>
|
||||||
</f7-link>
|
</f7-link>
|
||||||
<f7-link popover-open=".more-popover-menu">
|
<f7-link popover-open=".more-popover-menu">
|
||||||
<f7-icon f7="ellipsis_vertical" :class="{ 'tabbar-item-changed': query.type > 0 || query.amountFilter }"></f7-icon>
|
<f7-icon f7="ellipsis_vertical" :class="{ 'tabbar-item-changed': query.type > 0 || query.amountFilter || query.tagIds }"></f7-icon>
|
||||||
</f7-link>
|
</f7-link>
|
||||||
</f7-toolbar>
|
</f7-toolbar>
|
||||||
|
|
||||||
@@ -445,6 +445,36 @@
|
|||||||
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="query.amountFilter && query.amountFilter.startsWith(`${filterType.type}:`)"></f7-icon>
|
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="query.amountFilter && query.amountFilter.startsWith(`${filterType.type}:`)"></f7-icon>
|
||||||
</template>
|
</template>
|
||||||
</f7-list-item>
|
</f7-list-item>
|
||||||
|
|
||||||
|
<f7-list-item group-title :title="$t('Tags')" />
|
||||||
|
<f7-list-item :class="{ 'list-item-selected': !query.tagIds }" :title="$t('All')" @click="changeTagFilter('')">
|
||||||
|
<template #after>
|
||||||
|
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="!query.tagIds"></f7-icon>
|
||||||
|
</template>
|
||||||
|
</f7-list-item>
|
||||||
|
<f7-list-item :class="{ 'list-item-selected': query.tagIds && queryAllFilterTagIdsCount > 1 }"
|
||||||
|
:title="$t('Multiple Tags')" @click="filterMultipleTags()" v-if="allVisibleTagsCount > 0">
|
||||||
|
<template #after>
|
||||||
|
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="query.tagIds && queryAllFilterTagIdsCount > 1"></f7-icon>
|
||||||
|
</template>
|
||||||
|
</f7-list-item>
|
||||||
|
<f7-list-item :title="transactionTag.name"
|
||||||
|
:class="{ 'list-item-selected': query.tagIds === transactionTag.id, 'item-in-multiple-selection': queryAllFilterTagIdsCount > 1 && queryAllFilterTagIds[transactionTag.id] }"
|
||||||
|
:key="transactionTag.id"
|
||||||
|
v-for="transactionTag in allTransactionTags"
|
||||||
|
v-show="!transactionTag.hidden"
|
||||||
|
@click="changeTagFilter(transactionTag.id)"
|
||||||
|
>
|
||||||
|
<template #before-title>
|
||||||
|
<f7-icon f7="number" class="transaction-tag-name"></f7-icon>
|
||||||
|
</template>
|
||||||
|
<template #after>
|
||||||
|
<f7-icon class="list-item-checked-icon"
|
||||||
|
f7="checkmark_alt"
|
||||||
|
v-if="query.tagIds === transactionTag.id">
|
||||||
|
</f7-icon>
|
||||||
|
</template>
|
||||||
|
</f7-list-item>
|
||||||
</f7-list>
|
</f7-list>
|
||||||
</f7-popover>
|
</f7-popover>
|
||||||
|
|
||||||
@@ -555,12 +585,18 @@ export default {
|
|||||||
queryAllFilterAccountIds() {
|
queryAllFilterAccountIds() {
|
||||||
return this.transactionsStore.allFilterAccountIds;
|
return this.transactionsStore.allFilterAccountIds;
|
||||||
},
|
},
|
||||||
|
queryAllFilterTagIds() {
|
||||||
|
return this.transactionsStore.allFilterTagIds;
|
||||||
|
},
|
||||||
queryAllFilterCategoryIdsCount() {
|
queryAllFilterCategoryIdsCount() {
|
||||||
return this.transactionsStore.allFilterCategoryIdsCount;
|
return this.transactionsStore.allFilterCategoryIdsCount;
|
||||||
},
|
},
|
||||||
queryAllFilterAccountIdsCount() {
|
queryAllFilterAccountIdsCount() {
|
||||||
return this.transactionsStore.allFilterAccountIdsCount;
|
return this.transactionsStore.allFilterAccountIdsCount;
|
||||||
},
|
},
|
||||||
|
queryAllFilterTagIdsCount() {
|
||||||
|
return this.transactionsStore.allFilterTagIdsCount;
|
||||||
|
},
|
||||||
queryCategoryName() {
|
queryCategoryName() {
|
||||||
if (this.queryAllFilterCategoryIdsCount > 1) {
|
if (this.queryAllFilterCategoryIdsCount > 1) {
|
||||||
return this.$t('Multiple Categories');
|
return this.$t('Multiple Categories');
|
||||||
@@ -639,6 +675,9 @@ export default {
|
|||||||
allTransactionTags() {
|
allTransactionTags() {
|
||||||
return this.transactionTagsStore.allTransactionTagsMap;
|
return this.transactionTagsStore.allTransactionTagsMap;
|
||||||
},
|
},
|
||||||
|
allVisibleTagsCount() {
|
||||||
|
return this.transactionTagsStore.allVisibleTagsCount;
|
||||||
|
},
|
||||||
allDateRanges() {
|
allDateRanges() {
|
||||||
return datetimeConstants.allDateRanges;
|
return datetimeConstants.allDateRanges;
|
||||||
},
|
},
|
||||||
@@ -674,7 +713,8 @@ export default {
|
|||||||
minTime: dateRange ? dateRange.minTime : undefined,
|
minTime: dateRange ? dateRange.minTime : undefined,
|
||||||
type: parseInt(query.type) > 0 ? parseInt(query.type) : undefined,
|
type: parseInt(query.type) > 0 ? parseInt(query.type) : undefined,
|
||||||
categoryIds: query.categoryIds,
|
categoryIds: query.categoryIds,
|
||||||
accountIds: query.accountIds
|
accountIds: query.accountIds,
|
||||||
|
tagIds: query.tagIds
|
||||||
});
|
});
|
||||||
|
|
||||||
this.reload(null);
|
this.reload(null);
|
||||||
@@ -889,6 +929,21 @@ export default {
|
|||||||
this.reload(null);
|
this.reload(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
changeTagFilter(tagIds) {
|
||||||
|
if (this.query.tagIds === tagIds) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const changed = this.transactionsStore.updateTransactionListFilter({
|
||||||
|
tagIds: tagIds
|
||||||
|
});
|
||||||
|
|
||||||
|
this.showMorePopover = false;
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
this.reload(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
changeAmountFilter(filterType) {
|
changeAmountFilter(filterType) {
|
||||||
if (this.query.amountFilter === filterType) {
|
if (this.query.amountFilter === filterType) {
|
||||||
return;
|
return;
|
||||||
@@ -935,6 +990,9 @@ export default {
|
|||||||
filterMultipleAccounts() {
|
filterMultipleAccounts() {
|
||||||
this.f7router.navigate('/settings/filter/account?type=transactionListCurrent');
|
this.f7router.navigate('/settings/filter/account?type=transactionListCurrent');
|
||||||
},
|
},
|
||||||
|
filterMultipleTags() {
|
||||||
|
this.f7router.navigate('/settings/filter/tag?type=transactionListCurrent');
|
||||||
|
},
|
||||||
duplicate(transaction) {
|
duplicate(transaction) {
|
||||||
this.f7router.navigate(`/transaction/add?id=${transaction.id}&type=${transaction.type}`);
|
this.f7router.navigate(`/transaction/add?id=${transaction.id}&type=${transaction.type}`);
|
||||||
},
|
},
|
||||||
@@ -1170,6 +1228,11 @@ export default {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.more-popover-menu .transaction-tag-name {
|
||||||
|
padding-right: 4px;
|
||||||
|
font-size: var(--f7-list-item-title-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
.date-popover-menu .popover-inner,
|
.date-popover-menu .popover-inner,
|
||||||
.category-popover-menu .popover-inner,
|
.category-popover-menu .popover-inner,
|
||||||
.account-popover-menu .popover-inner,
|
.account-popover-menu .popover-inner,
|
||||||
|
|||||||
Reference in New Issue
Block a user