add search box in transaction category page / dialog

This commit is contained in:
MaysWind
2025-12-14 01:05:42 +08:00
parent a12038e40c
commit b1cefa5a34
14 changed files with 279 additions and 298 deletions
@@ -1,79 +1,48 @@
<template>
<v-card :class="{ 'pa-sm-1 pa-md-2': dialogMode }">
<template #title>
<div class="d-flex align-center justify-center" v-if="dialogMode">
<div class="w-100 text-center">
<h4 class="text-h4">{{ tt(title) }}</h4>
</div>
<v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading || !hasAnyAvailableCategory" :icon="true">
<v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent">
<v-list>
<v-list-item :prepend-icon="mdiSelectAll"
:title="tt('Select All')"
:disabled="!hasAnyVisibleCategory"
@click="selectAllCategories"></v-list-item>
<v-list-item :prepend-icon="mdiSelect"
:title="tt('Select None')"
:disabled="!hasAnyVisibleCategory"
@click="selectNoneCategories"></v-list-item>
<v-list-item :prepend-icon="mdiSelectInverse"
:title="tt('Invert Selection')"
:disabled="!hasAnyVisibleCategory"
@click="selectInvertCategories"></v-list-item>
<v-divider class="my-2"/>
<v-list-item :prepend-icon="mdiSelectAll"
:title="tt('Select All Visible')"
:disabled="!hasAnyVisibleCategory"
@click="selectAllVisibleCategories"></v-list-item>
<v-divider class="my-2"/>
<v-list-item :prepend-icon="mdiEyeOutline"
:title="tt('Show Hidden Transaction Categories')"
v-if="!showHidden" @click="showHidden = true"></v-list-item>
<v-list-item :prepend-icon="mdiEyeOffOutline"
:title="tt('Hide Hidden Transaction Categories')"
v-if="showHidden" @click="showHidden = false"></v-list-item>
</v-list>
</v-menu>
</v-btn>
</div>
<div class="d-flex align-center" v-else-if="!dialogMode">
<span>{{ tt(title) }}</span>
<v-spacer/>
<v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading" :icon="true">
<v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent">
<v-list>
<v-list-item :prepend-icon="mdiSelectAll"
:title="tt('Select All')"
:disabled="!hasAnyVisibleCategory"
@click="selectAllCategories"></v-list-item>
<v-list-item :prepend-icon="mdiSelect"
:title="tt('Select None')"
:disabled="!hasAnyVisibleCategory"
@click="selectNoneCategories"></v-list-item>
<v-list-item :prepend-icon="mdiSelectInverse"
:title="tt('Invert Selection')"
:disabled="!hasAnyVisibleCategory"
@click="selectInvertCategories"></v-list-item>
<v-divider class="my-2"/>
<v-list-item :prepend-icon="mdiSelectAll"
:title="tt('Select All Visible')"
:disabled="!hasAnyVisibleCategory"
@click="selectAllVisibleCategories"></v-list-item>
<v-divider class="my-2"/>
<v-list-item :prepend-icon="mdiEyeOutline"
:title="tt('Show Hidden Transaction Categories')"
v-if="!showHidden" @click="showHidden = true"></v-list-item>
<v-list-item :prepend-icon="mdiEyeOffOutline"
:title="tt('Hide Hidden Transaction Categories')"
v-if="showHidden" @click="showHidden = false"></v-list-item>
</v-list>
</v-menu>
</v-btn>
</div>
<v-row>
<v-col cols="6">
<div :class="{ 'text-h4': dialogMode, 'text-wrap': true }">
{{ tt(title) }}
</div>
</v-col>
<v-col cols="6" class="d-flex align-center">
<v-spacer v-if="!dialogMode"/>
<v-text-field density="compact" :disabled="loading || !hasAnyAvailableCategory"
:prepend-inner-icon="mdiMagnify"
:placeholder="tt('Find category')"
v-model="filterContent"
v-if="dialogMode"></v-text-field>
<v-btn density="comfortable" color="default" variant="text" class="ms-2"
:disabled="loading || !hasAnyAvailableCategory" :icon="true">
<v-icon :icon="mdiDotsVertical" />
<v-menu activator="parent">
<v-list>
<v-list-item :prepend-icon="mdiSelectAll"
:title="tt('Select All')"
:disabled="!hasAnyVisibleCategory"
@click="selectAllCategories"></v-list-item>
<v-list-item :prepend-icon="mdiSelect"
:title="tt('Select None')"
:disabled="!hasAnyVisibleCategory"
@click="selectNoneCategories"></v-list-item>
<v-list-item :prepend-icon="mdiSelectInverse"
:title="tt('Invert Selection')"
:disabled="!hasAnyVisibleCategory"
@click="selectInvertCategories"></v-list-item>
<v-divider class="my-2"/>
<v-list-item :prepend-icon="mdiEyeOutline"
:title="tt('Show Hidden Transaction Categories')"
v-if="!showHidden" @click="showHidden = true"></v-list-item>
<v-list-item :prepend-icon="mdiEyeOffOutline"
:title="tt('Hide Hidden Transaction Categories')"
v-if="showHidden" @click="showHidden = false"></v-list-item>
</v-list>
</v-menu>
</v-btn>
</v-col>
</v-row>
</template>
<div v-if="loading">
@@ -83,22 +52,22 @@
<v-card-text :class="{ 'flex-grow-1 overflow-y-auto': dialogMode }" v-else-if="!loading">
<v-expansion-panels class="category-types" multiple v-model="expandCategoryTypes">
<v-expansion-panel :key="transactionType.type"
:value="transactionType.type"
<v-expansion-panel :key="categoryType"
:value="parseInt(categoryType) as CategoryType"
class="border"
v-for="transactionType in allTransactionCategories">
v-for="(categories, categoryType) in allVisibleTransactionCategories">
<v-expansion-panel-title class="expand-panel-title-with-bg py-0">
<span class="ms-3">{{ getCategoryTypeName(transactionType.type) }}</span>
<span class="ms-3">{{ getCategoryTypeName(parseInt(categoryType)) }}</span>
</v-expansion-panel-title>
<v-expansion-panel-text>
<v-list rounded density="comfortable" class="pa-0">
<div class="ms-5 py-3" v-if="!hasAvailableCategory[transactionType.type]">{{ tt('No available category') }}</div>
<div class="ms-5 py-3" v-if="!categories || !categories.length">{{ tt('No available category') }}</div>
<template :key="category.id"
v-for="(category, idx) in transactionType.allCategories">
<v-divider v-if="showHidden ? idx > 0 : (!category.hidden ? idx > transactionType.firstVisibleCategoryIndex : false)"/>
v-for="(category, idx) in categories">
<v-divider v-if="idx > 0"/>
<v-list-item v-if="showHidden || !category.hidden">
<v-list-item>
<template #prepend>
<v-checkbox :model-value="isSubCategoriesAllChecked(category, filterCategoryIds)"
:indeterminate="isSubCategoriesHasButNotAllChecked(category, filterCategoryIds)"
@@ -112,15 +81,15 @@
</template>
</v-list-item>
<v-divider v-if="(showHidden || !category.hidden) && ((showHidden && transactionType.allSubCategories[category.id]) || transactionType.allVisibleSubCategoryCounts[category.id])"/>
<v-divider v-if="category.subCategories && category.subCategories.length"/>
<v-list rounded density="comfortable" class="pa-0 ms-4"
v-if="(showHidden || !category.hidden) && ((showHidden && transactionType.allSubCategories[category.id]) || transactionType.allVisibleSubCategoryCounts[category.id])">
v-if="category.subCategories && category.subCategories.length">
<template :key="subCategory.id"
v-for="(subCategory, subIdx) in transactionType.allSubCategories[category.id]">
<v-divider v-if="showHidden ? subIdx > 0 : (!subCategory.hidden ? subIdx > (transactionType.allFirstVisibleSubCategoryIndexes[category.id] as number) : false)"/>
v-for="(subCategory, subIdx) in category.subCategories">
<v-divider v-if="subIdx > 0"/>
<v-list-item v-if="showHidden || !subCategory.hidden">
<v-list-item>
<template #prepend>
<v-checkbox :model-value="isCategoryChecked(subCategory, filterCategoryIds)"
@update:model-value="updateCategorySelected(subCategory, $event)">
@@ -143,7 +112,7 @@
<v-card-text class="overflow-y-visible" v-if="dialogMode">
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
<v-btn :disabled="!hasAnyVisibleCategory" @click="save">{{ tt(applyText) }}</v-btn>
<v-btn :disabled="!hasAnyAvailableCategory" @click="save">{{ tt(applyText) }}</v-btn>
<v-btn color="secondary" variant="tonal" @click="cancel">{{ tt('Cancel') }}</v-btn>
</div>
</v-card-text>
@@ -170,7 +139,6 @@ import type { TransactionCategory } from '@/models/transaction_category.ts';
import {
selectAllSubCategories,
selectAllVisible,
selectAll,
selectNone,
selectInvert,
@@ -179,6 +147,7 @@ import {
} from '@/lib/category.ts';
import {
mdiMagnify,
mdiSelectAll,
mdiSelect,
mdiSelectInverse,
@@ -205,13 +174,14 @@ const { tt } = useI18n();
const {
loading,
showHidden,
filterContent,
filterCategoryIds,
title,
applyText,
allTransactionCategories,
allVisibleTransactionCategories,
allVisibleTransactionCategoryMap,
hasAnyAvailableCategory,
hasAnyVisibleCategory,
hasAvailableCategory,
isCategoryChecked,
getCategoryTypeName,
loadFilterCategoryIds,
@@ -267,7 +237,7 @@ function updateAllSubCategoriesSelected(category: TransactionCategory, value: bo
}
function selectAllCategories(): void {
selectAll(filterCategoryIds.value, transactionCategoriesStore.allTransactionCategoriesMap);
selectAll(filterCategoryIds.value, allVisibleTransactionCategoryMap.value);
if (props.autoSave) {
save();
@@ -275,7 +245,7 @@ function selectAllCategories(): void {
}
function selectNoneCategories(): void {
selectNone(filterCategoryIds.value, transactionCategoriesStore.allTransactionCategoriesMap);
selectNone(filterCategoryIds.value, allVisibleTransactionCategoryMap.value);
if (props.autoSave) {
save();
@@ -283,15 +253,7 @@ function selectNoneCategories(): void {
}
function selectInvertCategories(): void {
selectInvert(filterCategoryIds.value, transactionCategoriesStore.allTransactionCategoriesMap);
if (props.autoSave) {
save();
}
}
function selectAllVisibleCategories(): void {
selectAllVisible(filterCategoryIds.value, transactionCategoriesStore.allTransactionCategoriesMap);
selectInvert(filterCategoryIds.value, allVisibleTransactionCategoryMap.value);
if (props.autoSave) {
save();