mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-19 01:04:25 +08:00
modify style
This commit is contained in:
@@ -0,0 +1,51 @@
|
|||||||
|
<template>
|
||||||
|
<div class="btn-vertical-group d-flex flex-column">
|
||||||
|
<v-btn border :key="idx"
|
||||||
|
:color="value === button.value ? 'primary' : 'default'"
|
||||||
|
:variant="value === button.value ? 'tonal' : 'outlined'" :disabled="disabled"
|
||||||
|
v-for="(button, idx) in buttons"
|
||||||
|
@click="value = button.value">
|
||||||
|
{{ button.name }}
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: [
|
||||||
|
'disabled',
|
||||||
|
'buttons',
|
||||||
|
'modelValue'
|
||||||
|
],
|
||||||
|
emits: [
|
||||||
|
'update:modelValue'
|
||||||
|
],
|
||||||
|
computed: {
|
||||||
|
value: {
|
||||||
|
get: function () {
|
||||||
|
return this.modelValue;
|
||||||
|
},
|
||||||
|
set: function (value) {
|
||||||
|
if (value === this.modelValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('update:modelValue', value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.btn-vertical-group .v-btn:not(:first-child) {
|
||||||
|
border-top-left-radius: inherit;
|
||||||
|
border-top-right-radius: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-vertical-group .v-btn:not(:last-child) {
|
||||||
|
border-bottom: 0;
|
||||||
|
border-bottom-left-radius: inherit;
|
||||||
|
border-bottom-right-radius: inherit;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+8
-1
@@ -23,8 +23,11 @@ import { VIcon } from 'vuetify/components/VIcon';
|
|||||||
import { VImg } from 'vuetify/components/VImg';
|
import { VImg } from 'vuetify/components/VImg';
|
||||||
import { VInput } from 'vuetify/components/VInput';
|
import { VInput } from 'vuetify/components/VInput';
|
||||||
import { VLabel } from 'vuetify/components/VLabel';
|
import { VLabel } from 'vuetify/components/VLabel';
|
||||||
|
import { VLayout } from 'vuetify/components/VLayout';
|
||||||
import { VList, VListGroup, VListImg, VListItem, VListItemAction, VListItemMedia, VListItemSubtitle, VListItemTitle, VListSubheader } from 'vuetify/components/VList';
|
import { VList, VListGroup, VListImg, VListItem, VListItemAction, VListItemMedia, VListItemSubtitle, VListItemTitle, VListSubheader } from 'vuetify/components/VList';
|
||||||
|
import { VMain } from 'vuetify/components/VMain';
|
||||||
import { VMenu } from 'vuetify/components/VMenu';
|
import { VMenu } from 'vuetify/components/VMenu';
|
||||||
|
import { VNavigationDrawer } from 'vuetify/components/VNavigationDrawer';
|
||||||
import { VOverlay } from 'vuetify/components/VOverlay';
|
import { VOverlay } from 'vuetify/components/VOverlay';
|
||||||
import { VPagination } from 'vuetify/components/VPagination';
|
import { VPagination } from 'vuetify/components/VPagination';
|
||||||
import { VProgressCircular } from 'vuetify/components/VProgressCircular';
|
import { VProgressCircular } from 'vuetify/components/VProgressCircular';
|
||||||
@@ -77,6 +80,7 @@ import {
|
|||||||
import PinCodeInput from '@/components/common/PinCodeInput.vue';
|
import PinCodeInput from '@/components/common/PinCodeInput.vue';
|
||||||
|
|
||||||
import ItemIcon from '@/components/desktop/ItemIcon.vue';
|
import ItemIcon from '@/components/desktop/ItemIcon.vue';
|
||||||
|
import BtnVerticalGroup from '@/components/desktop/BtnVerticalGroup.vue';
|
||||||
import AmountInput from '@/components/desktop/AmountInput.vue';
|
import AmountInput from '@/components/desktop/AmountInput.vue';
|
||||||
import StepsBar from '@/components/desktop/StepsBar.vue';
|
import StepsBar from '@/components/desktop/StepsBar.vue';
|
||||||
import ConfirmDialog from '@/components/desktop/ConfirmDialog.vue';
|
import ConfirmDialog from '@/components/desktop/ConfirmDialog.vue';
|
||||||
@@ -132,6 +136,7 @@ const vuetify = createVuetify({
|
|||||||
VImg,
|
VImg,
|
||||||
VInput,
|
VInput,
|
||||||
VLabel,
|
VLabel,
|
||||||
|
VLayout,
|
||||||
VList,
|
VList,
|
||||||
VListGroup,
|
VListGroup,
|
||||||
VListImg,
|
VListImg,
|
||||||
@@ -141,7 +146,9 @@ const vuetify = createVuetify({
|
|||||||
VListItemSubtitle,
|
VListItemSubtitle,
|
||||||
VListItemTitle,
|
VListItemTitle,
|
||||||
VListSubheader,
|
VListSubheader,
|
||||||
|
VMain,
|
||||||
VMenu,
|
VMenu,
|
||||||
|
VNavigationDrawer,
|
||||||
VOverlay,
|
VOverlay,
|
||||||
VPagination,
|
VPagination,
|
||||||
VProgressCircular,
|
VProgressCircular,
|
||||||
@@ -168,7 +175,6 @@ const vuetify = createVuetify({
|
|||||||
mdi
|
mdi
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
defaults: {
|
defaults: {
|
||||||
VAlert: {
|
VAlert: {
|
||||||
VBtn: {
|
VBtn: {
|
||||||
@@ -384,6 +390,7 @@ app.component('DraggableList', draggable);
|
|||||||
app.component('PinCodeInput', PinCodeInput);
|
app.component('PinCodeInput', PinCodeInput);
|
||||||
|
|
||||||
app.component('ItemIcon', ItemIcon);
|
app.component('ItemIcon', ItemIcon);
|
||||||
|
app.component('BtnVerticalGroup', BtnVerticalGroup);
|
||||||
app.component('AmountInput', AmountInput);
|
app.component('AmountInput', AmountInput);
|
||||||
app.component('StepsBar', StepsBar);
|
app.component('StepsBar', StepsBar);
|
||||||
app.component('ConfirmDialog', ConfirmDialog);
|
app.component('ConfirmDialog', ConfirmDialog);
|
||||||
|
|||||||
@@ -100,8 +100,10 @@ input[type=number] {
|
|||||||
min-inline-size: 100%;
|
min-inline-size: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.w-100-window-container.v-window > .v-window__container {
|
.title-and-toolbar {
|
||||||
width: 100%;
|
min-height: 38px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
row-gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Common class for replacing the default style of vuetify **/
|
/** Common class for replacing the default style of vuetify **/
|
||||||
@@ -153,6 +155,18 @@ input[type=number] {
|
|||||||
height: 1rem;
|
height: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.w-100-window-container.v-window > .v-window__container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-text-truncate.v-tab > .v-btn__content {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-no-margin .v-skeleton-loader__text {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/** Common class for replacing the default style of Materio **/
|
/** Common class for replacing the default style of Materio **/
|
||||||
.v-application,
|
.v-application,
|
||||||
.text-body-1,
|
.text-body-1,
|
||||||
@@ -195,6 +209,10 @@ input[type=number] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.v-btn-group--density-comfortable.v-btn-group {
|
||||||
|
height: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
/** Replacing the default style of @vuepic/vue-datepicker **/
|
/** Replacing the default style of @vuepic/vue-datepicker **/
|
||||||
.dp__theme_light {
|
.dp__theme_light {
|
||||||
--dp-primary-color: #c67e48;
|
--dp-primary-color: #c67e48;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
<v-row class="match-height">
|
<v-row class="match-height">
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-card>
|
<v-card>
|
||||||
<div class="d-flex flex-column flex-md-row">
|
<v-layout>
|
||||||
<div>
|
<v-navigation-drawer :permanent="alwaysShowNav" v-model="showNav">
|
||||||
<div class="mx-6 my-4">
|
<div class="mx-6 my-4">
|
||||||
<small>{{ $t('Data source') }}</small>
|
<small>{{ $t('Data source') }}</small>
|
||||||
<p class="text-body-1 mt-1 mb-3">
|
<p class="text-body-1 mt-1 mb-3">
|
||||||
@@ -11,14 +11,14 @@
|
|||||||
<span v-else-if="!loading && exchangeRatesData && !exchangeRatesData.referenceUrl">{{ exchangeRatesData.dataSource }}</span>
|
<span v-else-if="!loading && exchangeRatesData && !exchangeRatesData.referenceUrl">{{ exchangeRatesData.dataSource }}</span>
|
||||||
<span v-else-if="!loading && !exchangeRatesData">{{ $t('None') }}</span>
|
<span v-else-if="!loading && !exchangeRatesData">{{ $t('None') }}</span>
|
||||||
<span v-else-if="loading">
|
<span v-else-if="loading">
|
||||||
<v-skeleton-loader class="exchange-rates-summary-skeleton mt-3 mb-4" type="text" :loading="true"></v-skeleton-loader>
|
<v-skeleton-loader class="skeleton-no-margin mt-3 mb-4" type="text" :loading="true"></v-skeleton-loader>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<small v-if="exchangeRatesDataUpdateTime">{{ $t('Last Updated') }}</small>
|
<small v-if="exchangeRatesDataUpdateTime || loading">{{ $t('Last Updated') }}</small>
|
||||||
<p class="text-body-1 mt-1" v-if="exchangeRatesDataUpdateTime">
|
<p class="text-body-1 mt-1" v-if="exchangeRatesDataUpdateTime || loading">
|
||||||
<span v-if="!loading">{{ exchangeRatesDataUpdateTime }}</span>
|
<span v-if="!loading">{{ exchangeRatesDataUpdateTime }}</span>
|
||||||
<span v-if="loading">
|
<span v-if="loading">
|
||||||
<v-skeleton-loader class="exchange-rates-summary-skeleton mt-3 mb-5" type="text" :loading="true"></v-skeleton-loader>
|
<v-skeleton-loader class="skeleton-no-margin mt-3 mb-5" type="text" :loading="true"></v-skeleton-loader>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,82 +35,93 @@
|
|||||||
<v-tabs show-arrows class="mb-4" direction="vertical"
|
<v-tabs show-arrows class="mb-4" direction="vertical"
|
||||||
:disabled="loading" v-model="baseCurrency"
|
:disabled="loading" v-model="baseCurrency"
|
||||||
v-if="exchangeRatesData && exchangeRatesData.exchangeRates && exchangeRatesData.exchangeRates.length">
|
v-if="exchangeRatesData && exchangeRatesData.exchangeRates && exchangeRatesData.exchangeRates.length">
|
||||||
<v-tab :key="exchangeRate.currencyCode" :value="exchangeRate.currencyCode"
|
<v-tab class="tab-text-truncate" :key="exchangeRate.currencyCode" :value="exchangeRate.currencyCode"
|
||||||
v-for="exchangeRate in availableExchangeRates">
|
v-for="exchangeRate in availableExchangeRates">
|
||||||
{{ exchangeRate.currencyDisplayName }}
|
<div class="text-truncate">
|
||||||
<small class="smaller ml-1">{{ exchangeRate.currencyCode }}</small>
|
<span>{{ exchangeRate.currencyDisplayName }}</span>
|
||||||
|
<small class="smaller ml-1">{{ exchangeRate.currencyCode }}</small>
|
||||||
|
</div>
|
||||||
</v-tab>
|
</v-tab>
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
<div class="mx-6 mt-2 mb-4"
|
<div class="mx-6 mt-2 mb-4"
|
||||||
v-else-if="!exchangeRatesData || !exchangeRatesData.exchangeRates || !exchangeRatesData.exchangeRates.length">
|
v-else-if="!exchangeRatesData || !exchangeRatesData.exchangeRates || !exchangeRatesData.exchangeRates.length">
|
||||||
<span v-if="!loading">{{ $t('None') }}</span>
|
<span v-if="!loading">{{ $t('None') }}</span>
|
||||||
<v-skeleton-loader type="paragraph" :loading="loading" v-else-if="loading"></v-skeleton-loader>
|
<span v-else-if="loading">
|
||||||
|
<v-skeleton-loader class="skeleton-no-margin pt-2 pb-5" type="text"
|
||||||
|
:key="itemIdx" :loading="loading"
|
||||||
|
v-for="itemIdx in [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]"></v-skeleton-loader>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</v-navigation-drawer>
|
||||||
|
<v-main>
|
||||||
|
<v-window class="d-flex flex-grow-1 disable-tab-transition w-100-window-container" v-model="activeTab">
|
||||||
|
<v-window-item value="exchangeRatesPage">
|
||||||
|
<v-card variant="flat">
|
||||||
|
<template #title>
|
||||||
|
<div class="title-and-toolbar d-flex align-center">
|
||||||
|
<v-btn class="mr-3 d-md-none" density="compact" color="default" variant="plain"
|
||||||
|
:ripple="false" :icon="true" @click="showNav = !showNav">
|
||||||
|
<v-icon :icon="icons.menu" size="24" />
|
||||||
|
</v-btn>
|
||||||
|
<span>{{ $t('Exchange Rates Data') }}</span>
|
||||||
|
<v-btn density="compact" color="default" variant="text"
|
||||||
|
class="ml-2" :icon="true"
|
||||||
|
v-if="!loading" @click="reload">
|
||||||
|
<v-icon :icon="icons.refresh" size="24" />
|
||||||
|
<v-tooltip activator="parent">{{ $t('Refresh') }}</v-tooltip>
|
||||||
|
</v-btn>
|
||||||
|
<v-progress-circular indeterminate size="24" class="ml-2" v-if="loading"></v-progress-circular>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<v-window class="d-flex flex-grow-1 ml-md-5 disable-tab-transition w-100-window-container" v-model="activeTab">
|
<v-table class="exchange-rates-table table-striped" :hover="!loading">
|
||||||
<v-window-item value="exchangeRatesPage">
|
<thead>
|
||||||
<v-card variant="flat">
|
<tr>
|
||||||
<template #title>
|
<th class="text-uppercase">
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">
|
||||||
<span>{{ $t('Exchange Rates Data') }}</span>
|
<span>{{ $t('Currency') }}</span>
|
||||||
<v-btn density="compact" color="default" variant="text"
|
<v-spacer/>
|
||||||
class="ml-2" :icon="true"
|
<span>{{ $t('Amount') }}</span>
|
||||||
v-if="!loading" @click="reload">
|
</div>
|
||||||
<v-icon :icon="icons.refresh" size="24" />
|
</th>
|
||||||
<v-tooltip activator="parent">{{ $t('Refresh') }}</v-tooltip>
|
</tr>
|
||||||
</v-btn>
|
</thead>
|
||||||
<v-progress-circular indeterminate size="24" class="ml-2" v-if="loading"></v-progress-circular>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<v-table class="exchange-rates-table table-striped" :hover="!loading">
|
<tbody>
|
||||||
<thead>
|
<tr :key="itemIdx"
|
||||||
<tr>
|
v-for="itemIdx in (loading && (!exchangeRatesData || !exchangeRatesData.exchangeRates || exchangeRatesData.exchangeRates.length < 1) ? [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] : [])">
|
||||||
<th class="text-uppercase">
|
<td class="px-0">
|
||||||
<div class="d-flex align-center">
|
<v-skeleton-loader type="text" :loading="true"></v-skeleton-loader>
|
||||||
<span>{{ $t('Currency') }}</span>
|
</td>
|
||||||
<v-spacer/>
|
</tr>
|
||||||
<span>{{ $t('Amount') }}</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
<tr v-if="!loading && (!exchangeRatesData || !exchangeRatesData.exchangeRates || !exchangeRatesData.exchangeRates.length)">
|
||||||
<tr :key="itemIdx"
|
<td>{{ $t('No exchange rates data') }}</td>
|
||||||
v-for="itemIdx in (loading && (!exchangeRatesData || !exchangeRatesData.exchangeRates || exchangeRatesData.exchangeRates.length < 1) ? [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] : [])">
|
</tr>
|
||||||
<td class="px-0">
|
|
||||||
<v-skeleton-loader type="text" :loading="true"></v-skeleton-loader>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr v-if="!loading && (!exchangeRatesData || !exchangeRatesData.exchangeRates || !exchangeRatesData.exchangeRates.length)">
|
<tr class="exchange-rates-table-row-data" :key="exchangeRate.currencyCode"
|
||||||
<td>{{ $t('No exchange rates data') }}</td>
|
v-for="exchangeRate in availableExchangeRates">
|
||||||
</tr>
|
<td>
|
||||||
|
<div class="d-flex align-center">
|
||||||
<tr class="exchange-rates-table-row-data" :key="exchangeRate.currencyCode"
|
<span class="text-sm">{{ exchangeRate.currencyDisplayName }}</span>
|
||||||
v-for="exchangeRate in availableExchangeRates">
|
<span class="text-caption ml-1">{{ exchangeRate.currencyCode }}</span>
|
||||||
<td>
|
<v-spacer/>
|
||||||
<div class="d-flex align-center">
|
<v-btn class="hover-display px-2 ml-2 mr-3" color="default"
|
||||||
<span class="text-sm">{{ exchangeRate.currencyDisplayName }}</span>
|
density="comfortable" variant="text"
|
||||||
<span class="text-caption ml-1">{{ exchangeRate.currencyCode }}</span>
|
@click="setAsBaseline(exchangeRate.currencyCode, exchangeRate)">
|
||||||
<v-spacer/>
|
{{ $t('Set As Baseline') }}
|
||||||
<v-btn class="hover-display px-2 ml-2 mr-3" color="default"
|
</v-btn>
|
||||||
density="comfortable" variant="text"
|
<span>{{ getDisplayConvertedAmount(exchangeRate, isEnableThousandsSeparator) }}</span>
|
||||||
@click="setAsBaseline(exchangeRate.currencyCode, exchangeRate)">
|
</div>
|
||||||
{{ $t('Set As Baseline') }}
|
</td>
|
||||||
</v-btn>
|
</tr>
|
||||||
<span>{{ getDisplayConvertedAmount(exchangeRate, isEnableThousandsSeparator) }}</span>
|
</tbody>
|
||||||
</div>
|
</v-table>
|
||||||
</td>
|
</v-card>
|
||||||
</tr>
|
</v-window-item>
|
||||||
</tbody>
|
</v-window>
|
||||||
</v-table>
|
</v-main>
|
||||||
</v-card>
|
</v-layout>
|
||||||
</v-window-item>
|
|
||||||
</v-window>
|
|
||||||
</div>
|
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -119,6 +130,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { useDisplay } from 'vuetify';
|
||||||
|
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useSettingsStore } from '@/stores/setting.js';
|
import { useSettingsStore } from '@/stores/setting.js';
|
||||||
import { useUserStore } from '@/stores/user.js';
|
import { useUserStore } from '@/stores/user.js';
|
||||||
@@ -130,11 +143,13 @@ import {
|
|||||||
} from '@/lib/currency.js';
|
} from '@/lib/currency.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
mdiRefresh
|
mdiRefresh,
|
||||||
|
mdiMenu
|
||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
const { mdAndUp } = useDisplay();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -142,8 +157,11 @@ export default {
|
|||||||
baseCurrency: userStore.currentUserDefaultCurrency,
|
baseCurrency: userStore.currentUserDefaultCurrency,
|
||||||
baseAmount: '1',
|
baseAmount: '1',
|
||||||
loading: true,
|
loading: true,
|
||||||
|
alwaysShowNav: mdAndUp.value,
|
||||||
|
showNav: mdAndUp.value,
|
||||||
icons: {
|
icons: {
|
||||||
refresh: mdiRefresh
|
refresh: mdiRefresh,
|
||||||
|
menu: mdiMenu
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -166,6 +184,22 @@ export default {
|
|||||||
created() {
|
created() {
|
||||||
this.reload(false);
|
this.reload(false);
|
||||||
},
|
},
|
||||||
|
setup() {
|
||||||
|
const display = useDisplay();
|
||||||
|
|
||||||
|
return {
|
||||||
|
display: display
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'display.mdAndUp.value': function (newValue) {
|
||||||
|
this.alwaysShowNav = newValue;
|
||||||
|
|
||||||
|
if (!this.showNav) {
|
||||||
|
this.showNav = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
reload(force) {
|
reload(force) {
|
||||||
const self = this;
|
const self = this;
|
||||||
@@ -234,10 +268,6 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.exchange-rates-summary-skeleton .v-skeleton-loader__text {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.exchange-rates-table tr.exchange-rates-table-row-data .hover-display {
|
.exchange-rates-table tr.exchange-rates-table-row-data .hover-display {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<v-card-text>
|
<v-card-text>
|
||||||
<h5 class="text-2xl font-weight-medium text-primary">
|
<h5 class="text-2xl font-weight-medium text-primary">
|
||||||
<span v-if="!loadingOverview || (transactionOverview && transactionOverview.thisMonth && transactionOverview.thisMonth.valid)">{{ transactionOverview && transactionOverview.thisMonth ? getDisplayExpenseAmount(transactionOverview.thisMonth) : '-' }}</span>
|
<span v-if="!loadingOverview || (transactionOverview && transactionOverview.thisMonth && transactionOverview.thisMonth.valid)">{{ transactionOverview && transactionOverview.thisMonth ? getDisplayExpenseAmount(transactionOverview.thisMonth) : '-' }}</span>
|
||||||
<v-skeleton-loader class="d-inline-block overview-card-skeleton mt-4" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!transactionOverview || !transactionOverview.thisMonth || !transactionOverview.thisMonth.valid)"></v-skeleton-loader>
|
<v-skeleton-loader class="d-inline-block skeleton-no-margin mt-3 pb-1" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!transactionOverview || !transactionOverview.thisMonth || !transactionOverview.thisMonth.valid)"></v-skeleton-loader>
|
||||||
<v-btn class="ml-1" density="compact" color="default" variant="text"
|
<v-btn class="ml-1" density="compact" color="default" variant="text"
|
||||||
:icon="true" @click="showAmountInHomePage = !showAmountInHomePage">
|
:icon="true" @click="showAmountInHomePage = !showAmountInHomePage">
|
||||||
<v-icon :icon="showAmountInHomePage ? icons.eyeSlash : icons.eye" size="20" />
|
<v-icon :icon="showAmountInHomePage ? icons.eyeSlash : icons.eye" size="20" />
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<div class="mt-2 mb-3">
|
<div class="mt-2 mb-3">
|
||||||
<span class="mr-2">{{ $t('Monthly income') }}</span>
|
<span class="mr-2">{{ $t('Monthly income') }}</span>
|
||||||
<span v-if="!loadingOverview || (transactionOverview && transactionOverview.thisMonth && transactionOverview.thisMonth.valid)">{{ transactionOverview && transactionOverview.thisMonth ? getDisplayIncomeAmount(transactionOverview.thisMonth) : '-' }}</span>
|
<span v-if="!loadingOverview || (transactionOverview && transactionOverview.thisMonth && transactionOverview.thisMonth.valid)">{{ transactionOverview && transactionOverview.thisMonth ? getDisplayIncomeAmount(transactionOverview.thisMonth) : '-' }}</span>
|
||||||
<v-skeleton-loader class="d-inline-block overview-card-skeleton" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!transactionOverview || !transactionOverview.thisMonth || !transactionOverview.thisMonth.valid)"></v-skeleton-loader>
|
<v-skeleton-loader class="d-inline-block skeleton-no-margin mt-1" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!transactionOverview || !transactionOverview.thisMonth || !transactionOverview.thisMonth.valid)"></v-skeleton-loader>
|
||||||
</div>
|
</div>
|
||||||
<v-btn size="small" to="/transaction/list?dateType=7">{{ $t('View Details') }}</v-btn>
|
<v-btn size="small" to="/transaction/list?dateType=7">{{ $t('View Details') }}</v-btn>
|
||||||
<v-img class="overview-card-background" src="img/desktop/card-background.png"/>
|
<v-img class="overview-card-background" src="img/desktop/card-background.png"/>
|
||||||
@@ -48,7 +48,8 @@
|
|||||||
|
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<h6 class="text-sm font-weight-medium mb-6">
|
<h6 class="text-sm font-weight-medium mb-6">
|
||||||
<span>{{ $t('format.misc.youHaveAccounts', { count: allAccounts.length }) }}</span>
|
<span v-if="!loadingOverview || (allAccounts && allAccounts.length)">{{ $t('format.misc.youHaveAccounts', { count: allAccounts.length }) }}</span>
|
||||||
|
<v-skeleton-loader class="skeleton-no-margin mt-1 mb-2 pb-1" width="200px" type="text" :loading="true" v-else-if="loadingOverview && (!allAccounts || !allAccounts.length)"></v-skeleton-loader>
|
||||||
</h6>
|
</h6>
|
||||||
|
|
||||||
<v-row>
|
<v-row>
|
||||||
@@ -63,7 +64,7 @@
|
|||||||
<div class="d-flex flex-column">
|
<div class="d-flex flex-column">
|
||||||
<span class="text-caption">{{ $t('Total assets') }}</span>
|
<span class="text-caption">{{ $t('Total assets') }}</span>
|
||||||
<span class="text-h6" v-if="!loadingOverview || (allAccounts && allAccounts.length)">{{ totalAssets }}</span>
|
<span class="text-h6" v-if="!loadingOverview || (allAccounts && allAccounts.length)">{{ totalAssets }}</span>
|
||||||
<v-skeleton-loader class="overview-card-skeleton mt-3 mb-2" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!allAccounts || !allAccounts.length)"></v-skeleton-loader>
|
<v-skeleton-loader class="skeleton-no-margin mt-3 mb-2" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!allAccounts || !allAccounts.length)"></v-skeleton-loader>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
@@ -79,7 +80,7 @@
|
|||||||
<div class="d-flex flex-column">
|
<div class="d-flex flex-column">
|
||||||
<span class="text-caption">{{ $t('Total liabilities') }}</span>
|
<span class="text-caption">{{ $t('Total liabilities') }}</span>
|
||||||
<span class="text-h6" v-if="!loadingOverview || (allAccounts && allAccounts.length)">{{ totalLiabilities }}</span>
|
<span class="text-h6" v-if="!loadingOverview || (allAccounts && allAccounts.length)">{{ totalLiabilities }}</span>
|
||||||
<v-skeleton-loader class="overview-card-skeleton mt-3 mb-2" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!allAccounts || !allAccounts.length)"></v-skeleton-loader>
|
<v-skeleton-loader class="skeleton-no-margin mt-3 mb-2" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!allAccounts || !allAccounts.length)"></v-skeleton-loader>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
@@ -95,7 +96,7 @@
|
|||||||
<div class="d-flex flex-column">
|
<div class="d-flex flex-column">
|
||||||
<span class="text-caption">{{ $t('Net assets') }}</span>
|
<span class="text-caption">{{ $t('Net assets') }}</span>
|
||||||
<span class="text-h6" v-if="!loadingOverview || (allAccounts && allAccounts.length)">{{ netAssets }}</span>
|
<span class="text-h6" v-if="!loadingOverview || (allAccounts && allAccounts.length)">{{ netAssets }}</span>
|
||||||
<v-skeleton-loader class="overview-card-skeleton mt-3 mb-2" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!allAccounts || !allAccounts.length)"></v-skeleton-loader>
|
<v-skeleton-loader class="skeleton-no-margin mt-3 mb-2" width="120px" type="text" :loading="true" v-else-if="loadingOverview && (!allAccounts || !allAccounts.length)"></v-skeleton-loader>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</v-col>
|
</v-col>
|
||||||
@@ -425,8 +426,4 @@ export default {
|
|||||||
inset-block-end: 0.5rem;
|
inset-block-end: 0.5rem;
|
||||||
inset-inline-end: 1rem;
|
inset-inline-end: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overview-card-skeleton .v-skeleton-loader__text {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -2,209 +2,214 @@
|
|||||||
<v-row class="match-height">
|
<v-row class="match-height">
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-card>
|
<v-card>
|
||||||
<div class="d-flex flex-column flex-md-row">
|
<v-layout>
|
||||||
<div>
|
<v-navigation-drawer :permanent="alwaysShowNav" v-model="showNav">
|
||||||
<div class="mx-6 my-4">
|
<div class="mx-6 my-4">
|
||||||
<small>{{ $t('Net assets') }}</small>
|
<small>{{ $t('Net assets') }}</small>
|
||||||
<p class="text-body-1 text-income mt-1 mb-3">
|
<p class="text-body-1 text-income text-truncate mt-1 mb-3">
|
||||||
<span v-if="!loading">{{ netAssets }}</span>
|
<span v-if="!loading">{{ netAssets }}</span>
|
||||||
<span v-else-if="loading">
|
<span v-else-if="loading">
|
||||||
<v-skeleton-loader class="accounts-summary-skeleton mt-3 mb-4" type="text" :loading="true"></v-skeleton-loader>
|
<v-skeleton-loader class="skeleton-no-margin pt-2 pb-1" type="text" :loading="true"></v-skeleton-loader>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<small>{{ $t('Total liabilities') }}</small>
|
<small>{{ $t('Total liabilities') }}</small>
|
||||||
<p class="text-body-1 text-expense mt-1 mb-3">
|
<p class="text-body-1 text-expense text-truncate mt-1 mb-3">
|
||||||
<span v-if="!loading">{{ totalLiabilities }}</span>
|
<span v-if="!loading">{{ totalLiabilities }}</span>
|
||||||
<span v-else-if="loading">
|
<span v-else-if="loading">
|
||||||
<v-skeleton-loader class="accounts-summary-skeleton mt-3 mb-4" type="text" :loading="true"></v-skeleton-loader>
|
<v-skeleton-loader class="skeleton-no-margin pt-2 pb-1" type="text" :loading="true"></v-skeleton-loader>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<small>{{ $t('Total assets') }}</small>
|
<small>{{ $t('Total assets') }}</small>
|
||||||
<p class="text-body-1 mt-1">
|
<p class="text-body-1 mt-1">
|
||||||
<span v-if="!loading">{{ totalAssets }}</span>
|
<span v-if="!loading">{{ totalAssets }}</span>
|
||||||
<span v-else-if="loading">
|
<span v-else-if="loading">
|
||||||
<v-skeleton-loader class="accounts-summary-skeleton mt-3 mb-5" type="text" :loading="true"></v-skeleton-loader>
|
<v-skeleton-loader class="skeleton-no-margin pt-2 pb-1" type="text" :loading="true"></v-skeleton-loader>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<v-divider />
|
<v-divider />
|
||||||
<v-tabs show-arrows class="account-category-tabs my-4" direction="vertical"
|
<v-tabs show-arrows class="account-category-tabs my-4" direction="vertical"
|
||||||
:disabled="loading" v-model="activeAccountCategoryId">
|
:disabled="loading" v-model="activeAccountCategoryId">
|
||||||
<v-tab :key="accountCategory.id" :value="accountCategory.id"
|
<v-tab class="tab-text-truncate" :key="accountCategory.id" :value="accountCategory.id"
|
||||||
v-for="accountCategory in allAccountCategories">
|
v-for="accountCategory in allAccountCategories">
|
||||||
<ItemIcon icon-type="account" :icon-id="accountCategory.defaultAccountIconId" />
|
<ItemIcon icon-type="account" :icon-id="accountCategory.defaultAccountIconId" />
|
||||||
<div class="ml-2 d-flex flex-column">
|
<div class="d-flex flex-column text-truncate ml-2">
|
||||||
<small class="text-left smaller">{{ accountCategoryTotalBalance(accountCategory) }}</small>
|
<small class="text-truncate text-left smaller">{{ accountCategoryTotalBalance(accountCategory) }}</small>
|
||||||
<div class="text-left">{{ $t(accountCategory.name) }}</div>
|
<span class="text-truncate text-left">{{ $t(accountCategory.name) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</v-tab>
|
</v-tab>
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
</div>
|
</v-navigation-drawer>
|
||||||
<v-window class="d-flex flex-grow-1 ml-md-5 disable-tab-transition w-100-window-container" v-model="activeTab">
|
<v-main>
|
||||||
<v-window-item value="accountPage">
|
<v-window class="d-flex flex-grow-1 disable-tab-transition w-100-window-container" v-model="activeTab">
|
||||||
<v-card variant="flat">
|
<v-window-item value="accountPage">
|
||||||
<template #title>
|
<v-card variant="flat" min-height="680">
|
||||||
<div class="d-flex align-center">
|
<template #title>
|
||||||
<span>{{ $t('Account List') }}</span>
|
<div class="title-and-toolbar d-flex align-center">
|
||||||
<v-btn class="ml-3" color="default" variant="outlined"
|
<v-btn class="mr-3 d-md-none" density="compact" color="default" variant="plain"
|
||||||
:disabled="loading" @click="add">{{ $t('Add') }}</v-btn>
|
:ripple="false" :icon="true" @click="showNav = !showNav">
|
||||||
<v-btn class="ml-3" color="primary" variant="tonal"
|
<v-icon :icon="icons.menu" size="24" />
|
||||||
:disabled="loading" @click="saveSortResult"
|
</v-btn>
|
||||||
v-if="displayOrderModified">{{ $t('Save Display Order') }}</v-btn>
|
<span>{{ $t('Account List') }}</span>
|
||||||
<v-btn density="compact" color="default" variant="text"
|
<v-btn class="ml-3" color="default" variant="outlined"
|
||||||
class="ml-2" :icon="true" :disabled="loading"
|
:disabled="loading" @click="add">{{ $t('Add') }}</v-btn>
|
||||||
v-if="!loading" @click="reload">
|
<v-btn class="ml-3" color="primary" variant="tonal"
|
||||||
<v-icon :icon="icons.refresh" size="24" />
|
:disabled="loading" @click="saveSortResult"
|
||||||
<v-tooltip activator="parent">{{ $t('Refresh') }}</v-tooltip>
|
v-if="displayOrderModified">{{ $t('Save Display Order') }}</v-btn>
|
||||||
</v-btn>
|
<v-btn density="compact" color="default" variant="text"
|
||||||
<v-progress-circular indeterminate size="24" class="ml-2" v-if="loading"></v-progress-circular>
|
class="ml-2" :icon="true" :disabled="loading"
|
||||||
<v-spacer/>
|
v-if="!loading" @click="reload">
|
||||||
<v-btn density="comfortable" color="default" variant="text" class="ml-2"
|
<v-icon :icon="icons.refresh" size="24" />
|
||||||
:disabled="loading" :icon="true">
|
<v-tooltip activator="parent">{{ $t('Refresh') }}</v-tooltip>
|
||||||
<v-icon :icon="icons.more" />
|
</v-btn>
|
||||||
<v-menu activator="parent">
|
<v-progress-circular indeterminate size="24" class="ml-2" v-if="loading"></v-progress-circular>
|
||||||
<v-list>
|
<v-spacer/>
|
||||||
<v-list-item :prepend-icon="icons.show"
|
<v-btn density="comfortable" color="default" variant="text" class="ml-2"
|
||||||
:title="$t('Show Hidden Account')"
|
:disabled="loading" :icon="true">
|
||||||
v-if="!showHidden" @click="showHidden = true"></v-list-item>
|
<v-icon :icon="icons.more" />
|
||||||
<v-list-item :prepend-icon="icons.hide"
|
<v-menu activator="parent">
|
||||||
:title="$t('Hide Hidden Account')"
|
<v-list>
|
||||||
v-if="showHidden" @click="showHidden = false"></v-list-item>
|
<v-list-item :prepend-icon="icons.show"
|
||||||
</v-list>
|
:title="$t('Show Hidden Account')"
|
||||||
</v-menu>
|
v-if="!showHidden" @click="showHidden = true"></v-list-item>
|
||||||
|
<v-list-item :prepend-icon="icons.hide"
|
||||||
|
:title="$t('Hide Hidden Account')"
|
||||||
|
v-if="showHidden" @click="showHidden = false"></v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<v-card-text class="accounts-overview-title text-truncate pt-0">
|
||||||
|
<span class="text-subtitle-1">{{ $t('Balance') }}</span>
|
||||||
|
<v-skeleton-loader class="skeleton-no-margin ml-3 mb-2" width="120px" type="text" :loading="true" v-if="loading && !hasAccount(activeAccountCategory)"></v-skeleton-loader>
|
||||||
|
<span class="accounts-overview-amount ml-3" v-else-if="!loading || hasAccount(activeAccountCategory)">{{ activeAccountCategoryTotalBalance }}</span>
|
||||||
|
<v-btn class="ml-2" density="compact" color="default" variant="text"
|
||||||
|
:icon="true" :disabled="loading"
|
||||||
|
@click="showAccountBalance = !showAccountBalance">
|
||||||
|
<v-icon :icon="showAccountBalance ? icons.eyeSlash : icons.eye" size="20" />
|
||||||
|
<v-tooltip activator="parent">{{ showAccountBalance ? $t('Hide Account Balance') : $t('Show Account Balance') }}</v-tooltip>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<div class="pl-2 pr-2 pr-md-4 mt-n4" v-if="loading && !hasAccount(activeAccountCategory)">
|
||||||
|
<v-skeleton-loader type="paragraph" :loading="loading"
|
||||||
|
:key="itemIdx" v-for="itemIdx in [ 1, 2, 3 ]"></v-skeleton-loader>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
|
|
||||||
<v-card-text class="accounts-overview-title pt-0">
|
<v-row class="pl-2 pr-2 pr-md-4" v-if="!loading && !hasAccount(activeAccountCategory)">
|
||||||
<span class="text-subtitle-1">{{ $t('Balance') }}</span>
|
<v-col cols="12">
|
||||||
<span class="accounts-overview-amount ml-3">
|
{{ $t('No available account') }}
|
||||||
{{ activeAccountCategoryTotalBalance }}
|
</v-col>
|
||||||
</span>
|
</v-row>
|
||||||
<v-btn class="ml-2" density="compact" color="default" variant="text"
|
|
||||||
:icon="true" :disabled="loading"
|
|
||||||
@click="showAccountBalance = !showAccountBalance">
|
|
||||||
<v-icon :icon="showAccountBalance ? icons.eyeSlash : icons.eye" size="20" />
|
|
||||||
<v-tooltip activator="parent">{{ showAccountBalance ? $t('Hide Account Balance') : $t('Show Account Balance') }}</v-tooltip>
|
|
||||||
</v-btn>
|
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<div v-if="loading && !hasAccount(activeAccountCategory)">
|
<v-row class="pl-6 pr-6 pr-md-8">
|
||||||
<v-skeleton-loader type="paragraph" :loading="loading"
|
<v-col cols="12">
|
||||||
:key="itemIdx" v-for="itemIdx in [ 1, 2, 3 ]"></v-skeleton-loader>
|
<draggable-list
|
||||||
</div>
|
class="list-group"
|
||||||
|
item-key="id"
|
||||||
|
handle=".drag-handle"
|
||||||
|
ghost-class="dragging-item"
|
||||||
|
:disabled="activeAccountCategoryVisibleAccountCount <= 1"
|
||||||
|
:list="categorizedAccounts[activeAccountCategory.id].accounts"
|
||||||
|
v-if="categorizedAccounts[activeAccountCategory.id] && categorizedAccounts[activeAccountCategory.id].accounts && categorizedAccounts[activeAccountCategory.id].accounts.length"
|
||||||
|
@change="onMove"
|
||||||
|
>
|
||||||
|
<template #item="{ element }">
|
||||||
|
<div class="list-group-item">
|
||||||
|
<v-card border class="card-title-with-bg account-card mb-8 h-auto" v-if="showHidden || !element.hidden">
|
||||||
|
<template #title>
|
||||||
|
<div class="account-title d-flex align-baseline">
|
||||||
|
<ItemIcon size="1.5rem" icon-type="account" :icon-id="element.icon"
|
||||||
|
:color="element.color" :hidden-status="element.hidden" />
|
||||||
|
<span class="account-name text-truncate ml-2">{{ element.name }}</span>
|
||||||
|
<small class="account-currency text-truncate ml-2">
|
||||||
|
{{ accountCurrency(element) }}
|
||||||
|
</small>
|
||||||
|
<v-spacer/>
|
||||||
|
<span class="align-self-center">
|
||||||
|
<v-icon :class="!loading && activeAccountCategoryVisibleAccountCount > 1 ? 'drag-handle' : 'disabled'"
|
||||||
|
:icon="icons.drag"/>
|
||||||
|
<v-tooltip activator="parent" v-if="!loading && activeAccountCategoryVisibleAccountCount > 1">{{ $t('Drag and Drop to Change Order') }}</v-tooltip>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<v-row class="pl-4 pr-4 pr-md-8" v-if="!loading && !hasAccount(activeAccountCategory)">
|
<div class="mt-4" v-if="element.type === allAccountTypes.MultiSubAccounts">
|
||||||
<v-col cols="12">
|
<v-btn-toggle
|
||||||
{{ $t('No available account') }}
|
class="account-subaccounts"
|
||||||
</v-col>
|
variant="outlined"
|
||||||
</v-row>
|
color="primary"
|
||||||
|
density="compact"
|
||||||
|
mandatory="force"
|
||||||
|
divided rounded="xl"
|
||||||
|
:disabled="loading"
|
||||||
|
v-model="activeSubAccount[element.id]"
|
||||||
|
>
|
||||||
|
<v-btn :value="undefined">
|
||||||
|
<span>{{ $t('All') }}</span>
|
||||||
|
</v-btn>
|
||||||
|
<v-btn :key="subAccount.id" :value="subAccount.id"
|
||||||
|
v-for="subAccount in element.subAccounts"
|
||||||
|
v-show="showHidden || !subAccount.hidden">
|
||||||
|
<ItemIcon size="1.5rem" icon-type="account" :icon-id="subAccount.icon"
|
||||||
|
:color="subAccount.color" :hidden-status="subAccount.hidden" />
|
||||||
|
<span class="ml-2">{{ subAccount.name }}</span>
|
||||||
|
</v-btn>
|
||||||
|
</v-btn-toggle>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<v-row class="pl-4 pr-4 pr-md-8">
|
<v-divider/>
|
||||||
<v-col cols="12">
|
|
||||||
<draggable-list
|
|
||||||
class="list-group"
|
|
||||||
item-key="id"
|
|
||||||
handle=".drag-handle"
|
|
||||||
ghost-class="dragging-item"
|
|
||||||
:disabled="activeAccountCategoryVisibleAccountCount <= 1"
|
|
||||||
:list="categorizedAccounts[activeAccountCategory.id].accounts"
|
|
||||||
v-if="categorizedAccounts[activeAccountCategory.id] && categorizedAccounts[activeAccountCategory.id].accounts && categorizedAccounts[activeAccountCategory.id].accounts.length"
|
|
||||||
@change="onMove"
|
|
||||||
>
|
|
||||||
<template #item="{ element }">
|
|
||||||
<div class="list-group-item">
|
|
||||||
<v-card border class="card-title-with-bg account-card mb-8 h-auto" v-if="showHidden || !element.hidden">
|
|
||||||
<template #title>
|
|
||||||
<div class="account-title d-flex align-baseline">
|
|
||||||
<ItemIcon size="1.5rem" icon-type="account" :icon-id="element.icon"
|
|
||||||
:color="element.color" :hidden-status="element.hidden" />
|
|
||||||
<span class="account-name ml-2">{{ element.name }}</span>
|
|
||||||
<small class="account-currency ml-2">
|
|
||||||
{{ accountCurrency(element) }}
|
|
||||||
</small>
|
|
||||||
<v-spacer/>
|
|
||||||
<span class="align-self-center">
|
|
||||||
<v-icon :class="!loading && activeAccountCategoryVisibleAccountCount > 1 ? 'drag-handle' : 'disabled'"
|
|
||||||
:icon="icons.drag"/>
|
|
||||||
<v-tooltip activator="parent" v-if="!loading && activeAccountCategoryVisibleAccountCount > 1">{{ $t('Drag and Drop to Change Order') }}</v-tooltip>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-4" v-if="element.type === allAccountTypes.MultiSubAccounts">
|
<v-card-text v-if="accountComment(element)">
|
||||||
<v-btn-toggle
|
{{ accountComment(element) }}
|
||||||
class="account-subaccounts"
|
</v-card-text>
|
||||||
variant="outlined"
|
|
||||||
color="primary"
|
<v-card-text>
|
||||||
density="compact"
|
<div class="d-flex account-toolbar align-center">
|
||||||
mandatory="force"
|
<v-btn class="px-2" density="comfortable" color="default" variant="text"
|
||||||
divided rounded="xl"
|
:disabled="loading" :prepend-icon="icons.transactions"
|
||||||
:disabled="loading"
|
:to="`/transaction/list?accountId=${accountOrSubAccountId(element)}`">
|
||||||
v-model="activeSubAccount[element.id]"
|
{{ $t('Transaction List') }}
|
||||||
>
|
|
||||||
<v-btn :value="undefined">
|
|
||||||
<span>{{ $t('All') }}</span>
|
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn :key="subAccount.id" :value="subAccount.id"
|
<v-btn class="hover-display px-2 ml-2" density="comfortable" color="default" variant="text"
|
||||||
v-for="subAccount in element.subAccounts"
|
:disabled="loading" :prepend-icon="icons.edit"
|
||||||
v-show="showHidden || !subAccount.hidden">
|
@click="edit(element)">
|
||||||
<ItemIcon size="1.5rem" icon-type="account" :icon-id="subAccount.icon"
|
{{ $t('Edit') }}
|
||||||
:color="subAccount.color" :hidden-status="subAccount.hidden" />
|
|
||||||
<span class="ml-2">{{ subAccount.name }}</span>
|
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-btn-toggle>
|
<v-btn class="hover-display px-2 ml-2" density="comfortable" color="default" variant="text"
|
||||||
</div>
|
:disabled="loading" :prepend-icon="icons.hide"
|
||||||
</template>
|
v-if="!element.hidden"
|
||||||
|
@click="hide(element, true)">
|
||||||
<v-divider/>
|
{{ $t('Hide') }}
|
||||||
|
</v-btn>
|
||||||
<v-card-text v-if="accountComment(element)">
|
<v-btn class="hover-display px-2 ml-2" density="comfortable" color="default" variant="text"
|
||||||
{{ accountComment(element) }}
|
:disabled="loading" :prepend-icon="icons.show"
|
||||||
</v-card-text>
|
v-if="element.hidden"
|
||||||
|
@click="hide(element, false)">
|
||||||
<v-card-text>
|
{{ $t('Show') }}
|
||||||
<div class="d-flex account-toolbar align-center">
|
</v-btn>
|
||||||
<v-btn class="px-2" density="comfortable" color="default" variant="text"
|
<v-btn class="hover-display px-2 ml-2" density="comfortable" color="default" variant="text"
|
||||||
:disabled="loading" :prepend-icon="icons.transactions"
|
:disabled="loading" :prepend-icon="icons.remove"
|
||||||
:to="`/transaction/list?accountId=${accountOrSubAccountId(element)}`">
|
@click="remove(element)">
|
||||||
{{ $t('Transaction List') }}
|
{{ $t('Delete') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn class="hover-display px-2 ml-2" density="comfortable" color="default" variant="text"
|
<v-spacer/>
|
||||||
:disabled="loading" :prepend-icon="icons.edit"
|
<span class="account-balance ml-2">{{ accountBalance(element) }}</span>
|
||||||
@click="edit(element)">
|
</div>
|
||||||
{{ $t('Edit') }}
|
</v-card-text>
|
||||||
</v-btn>
|
</v-card>
|
||||||
<v-btn class="hover-display px-2 ml-2" density="comfortable" color="default" variant="text"
|
</div>
|
||||||
:disabled="loading" :prepend-icon="icons.hide"
|
</template>
|
||||||
v-if="!element.hidden"
|
</draggable-list>
|
||||||
@click="hide(element, true)">
|
</v-col>
|
||||||
{{ $t('Hide') }}
|
</v-row>
|
||||||
</v-btn>
|
</v-card>
|
||||||
<v-btn class="hover-display px-2 ml-2" density="comfortable" color="default" variant="text"
|
</v-window-item>
|
||||||
:disabled="loading" :prepend-icon="icons.show"
|
</v-window>
|
||||||
v-if="element.hidden"
|
</v-main>
|
||||||
@click="hide(element, false)">
|
</v-layout>
|
||||||
{{ $t('Show') }}
|
|
||||||
</v-btn>
|
|
||||||
<v-btn class="hover-display px-2 ml-2" density="comfortable" color="default" variant="text"
|
|
||||||
:disabled="loading" :prepend-icon="icons.remove"
|
|
||||||
@click="remove(element)">
|
|
||||||
{{ $t('Delete') }}
|
|
||||||
</v-btn>
|
|
||||||
<v-spacer/>
|
|
||||||
<span class="account-balance ml-2">{{ accountBalance(element) }}</span>
|
|
||||||
</div>
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</draggable-list>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-card>
|
|
||||||
</v-window-item>
|
|
||||||
</v-window>
|
|
||||||
</div>
|
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -214,6 +219,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { useDisplay } from 'vuetify';
|
||||||
|
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useSettingsStore } from '@/stores/setting.js';
|
import { useSettingsStore } from '@/stores/setting.js';
|
||||||
import { useUserStore } from '@/stores/user.js';
|
import { useUserStore } from '@/stores/user.js';
|
||||||
@@ -233,6 +240,7 @@ import {
|
|||||||
mdiEyeOutline,
|
mdiEyeOutline,
|
||||||
mdiEyeOffOutline,
|
mdiEyeOffOutline,
|
||||||
mdiRefresh,
|
mdiRefresh,
|
||||||
|
mdiMenu,
|
||||||
mdiPencilOutline,
|
mdiPencilOutline,
|
||||||
mdiDeleteOutline,
|
mdiDeleteOutline,
|
||||||
mdiListBoxOutline,
|
mdiListBoxOutline,
|
||||||
@@ -242,17 +250,22 @@ import {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
const { mdAndUp } = useDisplay();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activeAccountCategoryId: accountConstants.allCategories[0].id,
|
activeAccountCategoryId: accountConstants.allCategories[0].id,
|
||||||
activeTab: 'accountPage',
|
activeTab: 'accountPage',
|
||||||
activeSubAccount: {},
|
activeSubAccount: {},
|
||||||
loading: true,
|
loading: true,
|
||||||
displayOrderModified: false,
|
displayOrderModified: false,
|
||||||
|
alwaysShowNav: mdAndUp.value,
|
||||||
|
showNav: mdAndUp.value,
|
||||||
showHidden: false,
|
showHidden: false,
|
||||||
icons: {
|
icons: {
|
||||||
eye: mdiEyeOutline,
|
eye: mdiEyeOutline,
|
||||||
eyeSlash: mdiEyeOffOutline,
|
eyeSlash: mdiEyeOffOutline,
|
||||||
refresh: mdiRefresh,
|
refresh: mdiRefresh,
|
||||||
|
menu: mdiMenu,
|
||||||
edit: mdiPencilOutline,
|
edit: mdiPencilOutline,
|
||||||
show: mdiEyeOutline,
|
show: mdiEyeOutline,
|
||||||
hide: mdiEyeOffOutline,
|
hide: mdiEyeOffOutline,
|
||||||
@@ -328,6 +341,22 @@ export default {
|
|||||||
created() {
|
created() {
|
||||||
this.reload(false);
|
this.reload(false);
|
||||||
},
|
},
|
||||||
|
setup() {
|
||||||
|
const display = useDisplay();
|
||||||
|
|
||||||
|
return {
|
||||||
|
display: display
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'display.mdAndUp.value': function (newValue) {
|
||||||
|
this.alwaysShowNav = newValue;
|
||||||
|
|
||||||
|
if (!this.showNav) {
|
||||||
|
this.showNav = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
reload(force) {
|
reload(force) {
|
||||||
const self = this;
|
const self = this;
|
||||||
@@ -500,17 +529,13 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.accounts-summary-skeleton .v-skeleton-loader__text {
|
.account-category-tabs .v-tab {
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.account-category-tabs .v-tab.v-tab {
|
|
||||||
--v-btn-height: calc(var(--v-tabs-height) * 1.5);
|
--v-btn-height: calc(var(--v-tabs-height) * 1.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.accounts-overview-title {
|
.accounts-overview-title {
|
||||||
line-height: 2rem !important;
|
line-height: 2rem !important;
|
||||||
height: 46px;
|
min-height: 52px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,178 +2,170 @@
|
|||||||
<v-row class="match-height">
|
<v-row class="match-height">
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-card>
|
<v-card>
|
||||||
<div class="d-flex flex-column flex-md-row">
|
<v-layout>
|
||||||
<div>
|
<v-navigation-drawer ref="navbar" :permanent="alwaysShowNav" v-model="showNav">
|
||||||
<div class="mx-6 my-4">
|
<div class="mx-6 my-4">
|
||||||
<div class="transaction-type-buttons d-flex flex-column">
|
<btn-vertical-group :disabled="loading" :buttons="[
|
||||||
<v-btn border :color="activeCategoryType === allCategoryTypes.Expense ? 'primary' : 'default'"
|
{ name: $t('Expense'), value: allCategoryTypes.Expense },
|
||||||
:variant="activeCategoryType === allCategoryTypes.Expense ? 'tonal' : 'outlined'" :disabled="loading"
|
{ name: $t('Income'), value: allCategoryTypes.Income },
|
||||||
@click="switchActiveCategoryType(allCategoryTypes.Expense)">
|
{ name: $t('Transfer'), value: allCategoryTypes.Transfer }
|
||||||
{{ $t('Expense') }}
|
]" v-model="activeCategoryType" @update:modelValue="switchActiveCategoryType" />
|
||||||
</v-btn>
|
|
||||||
<v-btn border :color="activeCategoryType === allCategoryTypes.Income ? 'primary' : 'default'"
|
|
||||||
:variant="activeCategoryType === allCategoryTypes.Income ? 'tonal' : 'outlined'" :disabled="loading"
|
|
||||||
@click="switchActiveCategoryType(allCategoryTypes.Income)">
|
|
||||||
{{ $t('Income') }}
|
|
||||||
</v-btn>
|
|
||||||
<v-btn border :color="activeCategoryType === allCategoryTypes.Transfer ? 'primary' : 'default'"
|
|
||||||
:variant="activeCategoryType === allCategoryTypes.Transfer ? 'tonal' : 'outlined'" :disabled="loading"
|
|
||||||
@click="switchActiveCategoryType(allCategoryTypes.Transfer)">
|
|
||||||
{{ $t('Transfer') }}
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<v-divider />
|
<v-divider />
|
||||||
<v-tabs show-arrows class="my-4" direction="vertical"
|
<v-tabs show-arrows class="my-4" direction="vertical"
|
||||||
:disabled="loading" v-model="primaryCategoryId">
|
:disabled="loading" v-model="primaryCategoryId">
|
||||||
<v-tab value="0" @click="primaryCategoryId = '0'">
|
<v-tab class="tab-text-truncate" value="0" @click="primaryCategoryId = '0'">
|
||||||
{{ $t('Primary Categories') }}
|
<span class="text-truncate">{{ $t('Primary Categories') }}</span>
|
||||||
</v-tab>
|
</v-tab>
|
||||||
<template :key="category.id" v-for="category in primaryCategories">
|
<template :key="category.id" v-for="category in primaryCategories">
|
||||||
<v-tab :value="category.id" v-if="!category.hidden"
|
<v-tab class="tab-text-truncate" :value="category.id" v-if="!category.hidden"
|
||||||
@click="switchPrimaryCategory(category)">
|
@click="switchPrimaryCategory(category)">
|
||||||
<div class="d-flex align-center">
|
<span class="text-truncate">{{ category.name }}</span>
|
||||||
<span>{{ category.name }}</span>
|
|
||||||
</div>
|
|
||||||
</v-tab>
|
</v-tab>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="loading && (!primaryCategories || primaryCategories.length < 1)">
|
<template v-if="loading && (!primaryCategories || primaryCategories.length < 1)">
|
||||||
<v-skeleton-loader class="transaction-primary-category-skeleton mx-5 mt-4 mb-3" type="text"
|
<v-skeleton-loader class="skeleton-no-margin mx-5 mt-4 mb-3" type="text"
|
||||||
:key="itemIdx" :loading="true" v-for="itemIdx in [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]"></v-skeleton-loader>
|
:key="itemIdx" :loading="true" v-for="itemIdx in [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]"></v-skeleton-loader>
|
||||||
</template>
|
</template>
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
</div>
|
</v-navigation-drawer>
|
||||||
<v-window class="d-flex flex-grow-1 ml-md-5 disable-tab-transition w-100-window-container" v-model="activeTab">
|
<v-main>
|
||||||
<v-window-item value="categoryPage">
|
<v-window class="d-flex flex-grow-1 disable-tab-transition w-100-window-container" v-model="activeTab">
|
||||||
<v-card variant="flat">
|
<v-window-item value="categoryPage">
|
||||||
<template #title>
|
<v-card variant="flat" :min-height="cardMinHeight">
|
||||||
<div class="d-flex align-center">
|
<template #title>
|
||||||
<span>{{ $t('Transaction Categories') }}</span>
|
<div class="title-and-toolbar d-flex align-center">
|
||||||
<v-btn class="ml-3" color="default" variant="outlined"
|
<v-btn class="mr-3 d-md-none" density="compact" color="default" variant="plain"
|
||||||
:disabled="loading || updating" @click="add">{{ $t('Add') }}</v-btn>
|
:ripple="false" :icon="true" @click="showNav = !showNav">
|
||||||
<v-btn class="ml-3" color="primary" variant="tonal"
|
<v-icon :icon="icons.menu" size="24" />
|
||||||
:disabled="loading || updating" @click="saveSortResult"
|
</v-btn>
|
||||||
v-if="displayOrderModified">{{ $t('Save Display Order') }}</v-btn>
|
<span>{{ $t('Transaction Categories') }}</span>
|
||||||
<v-btn density="compact" color="default" variant="text"
|
<v-btn class="ml-3" color="default" variant="outlined"
|
||||||
class="ml-2" :icon="true" :disabled="loading || updating"
|
:disabled="loading || updating" @click="add">{{ $t('Add') }}</v-btn>
|
||||||
v-if="!loading" @click="reload">
|
<v-btn class="ml-3" color="primary" variant="tonal"
|
||||||
<v-icon :icon="icons.refresh" size="24" />
|
:disabled="loading || updating" @click="saveSortResult"
|
||||||
<v-tooltip activator="parent">{{ $t('Refresh') }}</v-tooltip>
|
v-if="displayOrderModified">{{ $t('Save Display Order') }}</v-btn>
|
||||||
</v-btn>
|
<v-btn density="compact" color="default" variant="text"
|
||||||
<v-progress-circular indeterminate size="24" class="ml-2" v-if="loading"></v-progress-circular>
|
class="ml-2" :icon="true" :disabled="loading || updating"
|
||||||
<v-spacer/>
|
v-if="!loading" @click="reload">
|
||||||
<v-btn density="comfortable" color="default" variant="text" class="ml-2"
|
<v-icon :icon="icons.refresh" size="24" />
|
||||||
:disabled="loading || updating" :icon="true">
|
<v-tooltip activator="parent">{{ $t('Refresh') }}</v-tooltip>
|
||||||
<v-icon :icon="icons.more" />
|
</v-btn>
|
||||||
<v-menu activator="parent">
|
<v-progress-circular indeterminate size="24" class="ml-2" v-if="loading"></v-progress-circular>
|
||||||
<v-list>
|
<v-spacer/>
|
||||||
<v-list-item :prepend-icon="icons.show"
|
<v-btn density="comfortable" color="default" variant="text" class="ml-2"
|
||||||
:title="$t('Show Hidden Transaction Category')"
|
:disabled="loading || updating" :icon="true">
|
||||||
v-if="!showHidden" @click="showHidden = true"></v-list-item>
|
<v-icon :icon="icons.more" />
|
||||||
<v-list-item :prepend-icon="icons.hide"
|
<v-menu activator="parent">
|
||||||
:title="$t('Hide Hidden Transaction Category')"
|
<v-list>
|
||||||
v-if="showHidden" @click="showHidden = false"></v-list-item>
|
<v-list-item :prepend-icon="icons.show"
|
||||||
</v-list>
|
:title="$t('Show Hidden Transaction Category')"
|
||||||
</v-menu>
|
v-if="!showHidden" @click="showHidden = true"></v-list-item>
|
||||||
</v-btn>
|
<v-list-item :prepend-icon="icons.hide"
|
||||||
</div>
|
:title="$t('Hide Hidden Transaction Category')"
|
||||||
</template>
|
v-if="showHidden" @click="showHidden = false"></v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<v-table class="transaction-category-table table-striped" :hover="!loading">
|
<v-table class="transaction-category-table table-striped" :hover="!loading">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-uppercase">
|
<th class="text-uppercase">
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">
|
||||||
<span>{{ $t('Category Name') }}</span>
|
<span>{{ $t('Category Name') }}</span>
|
||||||
<v-spacer/>
|
<v-spacer/>
|
||||||
<span>{{ $t('Operation') }}</span>
|
<span>{{ $t('Operation') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody v-if="loading && noAvailableCategory">
|
<tbody v-if="loading && noAvailableCategory">
|
||||||
<tr :key="itemIdx" v-for="itemIdx in [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]">
|
<tr :key="itemIdx" v-for="itemIdx in [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]">
|
||||||
<td class="px-0">
|
<td class="px-0">
|
||||||
<v-skeleton-loader type="text" :loading="true"></v-skeleton-loader>
|
<v-skeleton-loader type="text" :loading="true"></v-skeleton-loader>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
||||||
<tbody v-if="!loading && noAvailableCategory">
|
<tbody v-if="!loading && noAvailableCategory">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">
|
||||||
<span>{{ $t('No available category') }}</span>
|
<span>{{ $t('No available category') }}</span>
|
||||||
<v-btn class="ml-3" color="default" variant="outlined"
|
<v-btn class="ml-3" color="default" variant="outlined"
|
||||||
@click="showPresetDialog = true"
|
@click="showPresetDialog = true"
|
||||||
v-if="hasSubCategories && noCategory">
|
v-if="hasSubCategories && noCategory">
|
||||||
{{ $t('Add Default Categories') }}
|
{{ $t('Add Default Categories') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
||||||
<draggable-list tag="tbody"
|
<draggable-list tag="tbody"
|
||||||
item-key="id"
|
item-key="id"
|
||||||
handle=".drag-handle"
|
handle=".drag-handle"
|
||||||
ghost-class="dragging-item"
|
ghost-class="dragging-item"
|
||||||
:disabled="noAvailableCategory"
|
:disabled="noAvailableCategory"
|
||||||
v-model="categories"
|
v-model="categories"
|
||||||
@change="onMove">
|
@change="onMove">
|
||||||
<template #item="{ element }">
|
<template #item="{ element }">
|
||||||
<tr class="transaction-category-table-row text-sm" v-if="showHidden || !element.hidden">
|
<tr class="transaction-category-table-row text-sm" v-if="showHidden || !element.hidden">
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">
|
||||||
<div class="d-flex align-center" :class="{ 'cursor-pointer': isCategorySupportSwitch(element) }"
|
<div class="d-flex align-center" :class="{ 'cursor-pointer': isCategorySupportSwitch(element) }"
|
||||||
@click="switchPrimaryCategory(element)">
|
@click="switchPrimaryCategory(element)">
|
||||||
<ItemIcon icon-type="category"
|
<ItemIcon icon-type="category"
|
||||||
:icon-id="element.icon" :color="element.color"
|
:icon-id="element.icon" :color="element.color"
|
||||||
:hidden-status="element.hidden" />
|
:hidden-status="element.hidden" />
|
||||||
<span class="ml-2">{{ element.name }}</span>
|
<span class="ml-2">{{ element.name }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-spacer/>
|
||||||
|
|
||||||
|
<v-btn class="hover-display px-2" color="default"
|
||||||
|
density="comfortable" variant="text"
|
||||||
|
:prepend-icon="icons.edit"
|
||||||
|
:disabled="loading || updating"
|
||||||
|
@click="edit(element)">
|
||||||
|
{{ $t('Edit') }}
|
||||||
|
</v-btn>
|
||||||
|
<v-btn class="hover-display px-2 ml-2" color="default"
|
||||||
|
density="comfortable" variant="text"
|
||||||
|
:prepend-icon="element.hidden ? icons.show : icons.hide"
|
||||||
|
:loading="categoryHiding[element.id]"
|
||||||
|
:disabled="loading || updating"
|
||||||
|
@click="hide(element, !element.hidden)">
|
||||||
|
{{ element.hidden ? $t('Show') : $t('Hide') }}
|
||||||
|
</v-btn>
|
||||||
|
<v-btn class="hover-display px-2 ml-2" color="default"
|
||||||
|
density="comfortable" variant="text"
|
||||||
|
:prepend-icon="icons.remove"
|
||||||
|
:loading="categoryRemoving[element.id]"
|
||||||
|
:disabled="loading || updating"
|
||||||
|
@click="remove(element)">
|
||||||
|
{{ $t('Delete') }}
|
||||||
|
</v-btn>
|
||||||
|
<span>
|
||||||
|
<v-icon :class="!loading && !updating && availableCategoryCount > 1 ? 'drag-handle' : 'disabled'"
|
||||||
|
:icon="icons.drag"/>
|
||||||
|
<v-tooltip activator="parent" v-if="!loading && !updating && availableCategoryCount > 1">{{ $t('Drag and Drop to Change Order') }}</v-tooltip>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</td>
|
||||||
<v-spacer/>
|
</tr>
|
||||||
|
</template>
|
||||||
<v-btn class="hover-display px-2" color="default"
|
</draggable-list>
|
||||||
density="comfortable" variant="text"
|
</v-table>
|
||||||
:prepend-icon="icons.edit"
|
</v-card>
|
||||||
:disabled="loading || updating"
|
</v-window-item>
|
||||||
@click="edit(element)">
|
</v-window>
|
||||||
{{ $t('Edit') }}
|
</v-main>
|
||||||
</v-btn>
|
</v-layout>
|
||||||
<v-btn class="hover-display px-2 ml-2" color="default"
|
|
||||||
density="comfortable" variant="text"
|
|
||||||
:prepend-icon="element.hidden ? icons.show : icons.hide"
|
|
||||||
:loading="categoryHiding[element.id]"
|
|
||||||
:disabled="loading || updating"
|
|
||||||
@click="hide(element, !element.hidden)">
|
|
||||||
{{ element.hidden ? $t('Show') : $t('Hide') }}
|
|
||||||
</v-btn>
|
|
||||||
<v-btn class="hover-display px-2 ml-2" color="default"
|
|
||||||
density="comfortable" variant="text"
|
|
||||||
:prepend-icon="icons.remove"
|
|
||||||
:loading="categoryRemoving[element.id]"
|
|
||||||
:disabled="loading || updating"
|
|
||||||
@click="remove(element)">
|
|
||||||
{{ $t('Delete') }}
|
|
||||||
</v-btn>
|
|
||||||
<span>
|
|
||||||
<v-icon :class="!loading && !updating && availableCategoryCount > 1 ? 'drag-handle' : 'disabled'"
|
|
||||||
:icon="icons.drag"/>
|
|
||||||
<v-tooltip activator="parent" v-if="!loading && !updating && availableCategoryCount > 1">{{ $t('Drag and Drop to Change Order') }}</v-tooltip>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
</draggable-list>
|
|
||||||
</v-table>
|
|
||||||
</v-card>
|
|
||||||
</v-window-item>
|
|
||||||
</v-window>
|
|
||||||
</div>
|
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -188,13 +180,17 @@
|
|||||||
<script>
|
<script>
|
||||||
import PresetCategoryDialog from './list/dialogs/PresetCategoryDialog.vue';
|
import PresetCategoryDialog from './list/dialogs/PresetCategoryDialog.vue';
|
||||||
|
|
||||||
|
import { useDisplay } from 'vuetify';
|
||||||
|
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||||
|
|
||||||
import categoryConstants from '@/consts/category.js';
|
import categoryConstants from '@/consts/category.js';
|
||||||
|
import { getOuterHeight } from '@/lib/ui.desktop.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
mdiRefresh,
|
mdiRefresh,
|
||||||
|
mdiMenu,
|
||||||
mdiPencilOutline,
|
mdiPencilOutline,
|
||||||
mdiEyeOffOutline,
|
mdiEyeOffOutline,
|
||||||
mdiEyeOutline,
|
mdiEyeOutline,
|
||||||
@@ -208,6 +204,8 @@ export default {
|
|||||||
PresetCategoryDialog
|
PresetCategoryDialog
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
const { mdAndUp } = useDisplay();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activeCategoryType: categoryConstants.allCategoryTypes.Expense,
|
activeCategoryType: categoryConstants.allCategoryTypes.Expense,
|
||||||
activeTab: 'categoryPage',
|
activeTab: 'categoryPage',
|
||||||
@@ -217,10 +215,14 @@ export default {
|
|||||||
categoryHiding: {},
|
categoryHiding: {},
|
||||||
categoryRemoving: {},
|
categoryRemoving: {},
|
||||||
displayOrderModified: false,
|
displayOrderModified: false,
|
||||||
|
cardMinHeight: 680,
|
||||||
|
alwaysShowNav: mdAndUp.value,
|
||||||
|
showNav: mdAndUp.value,
|
||||||
showHidden: false,
|
showHidden: false,
|
||||||
showPresetDialog: false,
|
showPresetDialog: false,
|
||||||
icons: {
|
icons: {
|
||||||
refresh: mdiRefresh,
|
refresh: mdiRefresh,
|
||||||
|
menu: mdiMenu,
|
||||||
edit: mdiPencilOutline,
|
edit: mdiPencilOutline,
|
||||||
show: mdiEyeOutline,
|
show: mdiEyeOutline,
|
||||||
hide: mdiEyeOffOutline,
|
hide: mdiEyeOffOutline,
|
||||||
@@ -286,6 +288,22 @@ export default {
|
|||||||
created() {
|
created() {
|
||||||
this.reload(false);
|
this.reload(false);
|
||||||
},
|
},
|
||||||
|
setup() {
|
||||||
|
const display = useDisplay();
|
||||||
|
|
||||||
|
return {
|
||||||
|
display: display
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'display.mdAndUp.value': function (newValue) {
|
||||||
|
this.alwaysShowNav = newValue;
|
||||||
|
|
||||||
|
if (!this.showNav) {
|
||||||
|
this.showNav = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
reload(force) {
|
reload(force) {
|
||||||
const self = this;
|
const self = this;
|
||||||
@@ -301,6 +319,8 @@ export default {
|
|||||||
if (force) {
|
if (force) {
|
||||||
self.$refs.snackbar.showMessage('Category list has been updated');
|
self.$refs.snackbar.showMessage('Category list has been updated');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.updateCardMinHeight();
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
self.loading = false;
|
self.loading = false;
|
||||||
|
|
||||||
@@ -410,13 +430,9 @@ export default {
|
|||||||
this.reload(false);
|
this.reload(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
switchActiveCategoryType(activeCategoryType) {
|
switchActiveCategoryType() {
|
||||||
if (this.activeCategoryType === activeCategoryType) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.activeCategoryType = activeCategoryType;
|
|
||||||
this.primaryCategoryId = '0';
|
this.primaryCategoryId = '0';
|
||||||
|
this.updateCardMinHeight();
|
||||||
},
|
},
|
||||||
isCategorySupportSwitch(category) {
|
isCategorySupportSwitch(category) {
|
||||||
if (!category || category.hidden) {
|
if (!category || category.hidden) {
|
||||||
@@ -433,16 +449,25 @@ export default {
|
|||||||
if (!category.parentId || category.parentId === '' || category.parentId === '0') {
|
if (!category.parentId || category.parentId === '' || category.parentId === '0') {
|
||||||
this.primaryCategoryId = category.id;
|
this.primaryCategoryId = category.id;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
updateCardMinHeight() {
|
||||||
|
const self = this
|
||||||
|
|
||||||
|
self.$nextTick(() => {
|
||||||
|
if (self.$refs.navbar && self.$refs.navbar.$el && self.$refs.navbar.$el.nextElementSibling) {
|
||||||
|
let navbarHeight = getOuterHeight(self.$refs.navbar.$el.nextElementSibling);
|
||||||
|
|
||||||
|
if (navbarHeight > self.cardMinHeight) {
|
||||||
|
self.cardMinHeight = navbarHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.transaction-primary-category-skeleton .v-skeleton-loader__text {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.transaction-category-table tr.transaction-category-table-row .hover-display {
|
.transaction-category-table tr.transaction-category-table-row .hover-display {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-text class="mt-1 pb-2">
|
<v-card-text class="mt-1 pb-2">
|
||||||
<div class="font-weight-semibold text-truncate text-red text-h5 text-income me-2 mb-2" v-if="!loading || incomeAmount">{{ incomeAmount }}</div>
|
<div class="font-weight-semibold text-truncate text-red text-h5 text-income me-2 mb-2" v-if="!loading || incomeAmount">{{ incomeAmount }}</div>
|
||||||
<v-skeleton-loader class="income-expense-overview-card-skeleton mt-4 mb-6" type="text" width="120px" :loading="true" v-else-if="loading && !incomeAmount"></v-skeleton-loader>
|
<v-skeleton-loader class="skeleton-no-margin mt-4 mb-7" type="text" width="120px" :loading="true" v-else-if="loading && !incomeAmount"></v-skeleton-loader>
|
||||||
<div class="text-truncate text-h6 text-expense" v-if="!loading || expenseAmount">{{ expenseAmount }}</div>
|
<div class="text-truncate text-h6 text-expense" v-if="!loading || expenseAmount">{{ expenseAmount }}</div>
|
||||||
<v-skeleton-loader class="income-expense-overview-card-skeleton mb-2" type="text" width="120px" :loading="true" v-else-if="loading && !expenseAmount"></v-skeleton-loader>
|
<v-skeleton-loader class="skeleton-no-margin mb-2" type="text" width="120px" :loading="true" v-else-if="loading && !expenseAmount"></v-skeleton-loader>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
<v-card-text class="mt-6">
|
<v-card-text class="mt-6">
|
||||||
<span class="text-caption">{{ datetime }}</span>
|
<span class="text-caption">{{ datetime }}</span>
|
||||||
@@ -51,9 +51,3 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.income-expense-overview-card-skeleton .v-skeleton-loader__text {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -2,43 +2,36 @@
|
|||||||
<v-row class="match-height">
|
<v-row class="match-height">
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-card>
|
<v-card>
|
||||||
<div class="d-flex flex-column flex-md-row">
|
<v-layout>
|
||||||
<div>
|
<v-navigation-drawer :permanent="alwaysShowNav" v-model="showNav">
|
||||||
<div class="mx-6 my-4">
|
<div class="mx-6 my-4">
|
||||||
<v-btn-toggle
|
<btn-vertical-group :disabled="loading" :buttons="[
|
||||||
variant="outlined"
|
{ name: $t('Pie Chart'), value: allChartTypes.Pie },
|
||||||
color="primary"
|
{ name: $t('Bar Chart'), value: allChartTypes.Bar }
|
||||||
density="comfortable"
|
]" v-model="query.chartType" @update:modelValue="setChartType" />
|
||||||
mandatory="force"
|
|
||||||
divided
|
|
||||||
:disabled="loading"
|
|
||||||
v-model="query.chartType"
|
|
||||||
>
|
|
||||||
<v-btn :value="allChartTypes.Pie" @click="setChartType(allChartTypes.Pie)">
|
|
||||||
{{ $t('Pie Chart') }}
|
|
||||||
</v-btn>
|
|
||||||
<v-btn :value="allChartTypes.Bar" @click="setChartType(allChartTypes.Bar)">
|
|
||||||
{{ $t('Bar Chart') }}
|
|
||||||
</v-btn>
|
|
||||||
</v-btn-toggle>
|
|
||||||
</div>
|
</div>
|
||||||
<v-divider />
|
<v-divider />
|
||||||
<v-tabs show-arrows class="my-4" direction="vertical"
|
<v-tabs show-arrows class="my-4" direction="vertical"
|
||||||
:disabled="loading" v-model="query.chartDataType">
|
:disabled="loading" v-model="query.chartDataType">
|
||||||
<v-tab :key="dataType.type" :value="dataType.type"
|
<v-tab class="tab-text-truncate" :key="dataType.type" :value="dataType.type"
|
||||||
v-for="dataType in allChartDataTypes">
|
v-for="dataType in allChartDataTypes">
|
||||||
{{ $t(dataType.name) }}
|
<span class="text-truncate">{{ $t(dataType.name) }}</span>
|
||||||
|
<v-tooltip activator="parent" location="right">{{ $t(dataType.name) }}</v-tooltip>
|
||||||
</v-tab>
|
</v-tab>
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
</div>
|
</v-navigation-drawer>
|
||||||
<v-window class="d-flex flex-grow-1 ml-md-5 disable-tab-transition w-100-window-container" v-model="activeTab">
|
<v-main>
|
||||||
<v-window-item value="statisticsPage">
|
<v-window class="d-flex flex-grow-1 disable-tab-transition w-100-window-container" v-model="activeTab">
|
||||||
<v-card variant="flat">
|
<v-window-item value="statisticsPage">
|
||||||
<template #title>
|
<v-card variant="flat" min-height="680">
|
||||||
<div class="statistics-title d-flex align-center">
|
<template #title>
|
||||||
<span>{{ $t('Statistics Data') }}</span>
|
<div class="title-and-toolbar d-flex align-center">
|
||||||
<div class="ml-4">
|
<v-btn class="mr-3 d-md-none" density="compact" color="default" variant="plain"
|
||||||
<v-btn-group color="default" density="comfortable" variant="outlined" divided>
|
:ripple="false" :icon="true" @click="showNav = !showNav">
|
||||||
|
<v-icon :icon="icons.menu" size="24" />
|
||||||
|
</v-btn>
|
||||||
|
<span>{{ $t('Statistics Data') }}</span>
|
||||||
|
<v-btn-group class="ml-4" color="default" density="comfortable" variant="outlined" divided>
|
||||||
<v-btn :icon="icons.left"
|
<v-btn :icon="icons.left"
|
||||||
:disabled="loading || query.dateType === allDateRanges.All.type || query.chartDataType === allChartDataTypes.AccountTotalAssets.type || query.chartDataType === allChartDataTypes.AccountTotalLiabilities.type"
|
:disabled="loading || query.dateType === allDateRanges.All.type || query.chartDataType === allChartDataTypes.AccountTotalAssets.type || query.chartDataType === allChartDataTypes.AccountTotalLiabilities.type"
|
||||||
@click="shiftDateRange(query.startTime, query.endTime, -1)"/>
|
@click="shiftDateRange(query.startTime, query.endTime, -1)"/>
|
||||||
@@ -90,108 +83,108 @@
|
|||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-menu>
|
</v-menu>
|
||||||
|
<v-btn density="compact" color="default" variant="text"
|
||||||
|
class="ml-2" :icon="true" :disabled="loading"
|
||||||
|
v-if="!loading" @click="reload">
|
||||||
|
<v-icon :icon="icons.refresh" size="24" />
|
||||||
|
<v-tooltip activator="parent">{{ $t('Refresh') }}</v-tooltip>
|
||||||
|
</v-btn>
|
||||||
|
<v-progress-circular indeterminate size="24" class="ml-2" v-if="loading"></v-progress-circular>
|
||||||
|
<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.filter"
|
||||||
|
:title="$t('Filter Accounts')"
|
||||||
|
@click="showFilterAccountDialog = true"></v-list-item>
|
||||||
|
<v-list-item :prepend-icon="icons.filter"
|
||||||
|
:title="$t('Filter Transaction Categories')"
|
||||||
|
@click="showFilterCategoryDialog = true"></v-list-item>
|
||||||
|
<v-divider class="my-2"/>
|
||||||
|
<v-list-item to="/app/settings?tab=statisticsSetting"
|
||||||
|
:prepend-icon="icons.filterSettings"
|
||||||
|
:title="$t('Settings')"></v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
<v-btn density="compact" color="default" variant="text"
|
</template>
|
||||||
class="ml-2" :icon="true" :disabled="loading"
|
|
||||||
v-if="!loading" @click="reload">
|
<div v-if="initing">
|
||||||
<v-icon :icon="icons.refresh" size="24" />
|
<v-skeleton-loader type="paragraph" :loading="initing"
|
||||||
<v-tooltip activator="parent">{{ $t('Refresh') }}</v-tooltip>
|
:key="itemIdx" v-for="itemIdx in [ 1, 2, 3, 4 ]"></v-skeleton-loader>
|
||||||
</v-btn>
|
|
||||||
<v-progress-circular indeterminate size="24" class="ml-2" v-if="loading"></v-progress-circular>
|
|
||||||
<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.filter"
|
|
||||||
:title="$t('Filter Accounts')"
|
|
||||||
@click="showFilterAccountDialog = true"></v-list-item>
|
|
||||||
<v-list-item :prepend-icon="icons.filter"
|
|
||||||
:title="$t('Filter Transaction Categories')"
|
|
||||||
@click="showFilterCategoryDialog = true"></v-list-item>
|
|
||||||
<v-divider class="my-2"/>
|
|
||||||
<v-list-item to="/app/settings?tab=statisticsSetting"
|
|
||||||
:prepend-icon="icons.filterSettings"
|
|
||||||
:title="$t('Settings')"></v-list-item>
|
|
||||||
</v-list>
|
|
||||||
</v-menu>
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
|
|
||||||
<div v-if="initing">
|
<v-card-text class="statistics-overview-title pt-0" :class="{ 'disabled': loading }"
|
||||||
<v-skeleton-loader type="paragraph" :loading="initing"
|
v-if="!initing && statisticsData && statisticsData.items && statisticsData.items.length">
|
||||||
:key="itemIdx" v-for="itemIdx in [ 1, 2, 3, 4 ]"></v-skeleton-loader>
|
<span class="text-subtitle-1">{{ totalAmountName }}</span>
|
||||||
</div>
|
<span class="statistics-overview-amount ml-3" :class="statisticsTextColor">
|
||||||
|
{{ getDisplayAmount(statisticsData.totalAmount, defaultCurrency) }}
|
||||||
|
</span>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
<v-card-text class="statistics-overview-title pt-0" :class="{ 'disabled': loading }"
|
<v-card-text class="statistics-overview-title pt-0"
|
||||||
v-if="!initing && statisticsData && statisticsData.items && statisticsData.items.length">
|
v-else-if="!initing && (!statisticsData || !statisticsData.items || !statisticsData.items.length)">
|
||||||
<span class="text-subtitle-1">{{ totalAmountName }}</span>
|
<span class="text-subtitle-1 statistics-overview-empty-tip">{{ $t('No transaction data') }}</span>
|
||||||
<span class="statistics-overview-amount ml-3" :class="statisticsTextColor">
|
</v-card-text>
|
||||||
{{ getDisplayAmount(statisticsData.totalAmount, defaultCurrency) }}
|
|
||||||
</span>
|
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-text class="statistics-overview-title pt-0"
|
<v-card-text :class="{ 'readonly': loading }" v-if="!initing && query.chartType === allChartTypes.Pie">
|
||||||
v-else-if="!initing && (!statisticsData || !statisticsData.items || !statisticsData.items.length)">
|
<pie-chart
|
||||||
<span class="text-subtitle-1 statistics-overview-empty-tip">{{ $t('No transaction data') }}</span>
|
:items="statisticsData && statisticsData.items && statisticsData.items.length ? statisticsData.items : []"
|
||||||
</v-card-text>
|
:min-valid-percent="0.0001"
|
||||||
|
:show-value="showAmountInChart"
|
||||||
|
:enable-click-item="true"
|
||||||
|
:default-currency="defaultCurrency"
|
||||||
|
id-field="id"
|
||||||
|
name-field="name"
|
||||||
|
value-field="totalAmount"
|
||||||
|
percent-field="percent"
|
||||||
|
currency-field="currency"
|
||||||
|
hidden-field="hidden"
|
||||||
|
@click="clickPieChartItem"
|
||||||
|
/>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
<v-card-text :class="{ 'readonly': loading }" v-if="!initing && query.chartType === allChartTypes.Pie">
|
<v-card-text :class="{ 'readonly': loading }" v-if="!initing && query.chartType === allChartTypes.Bar">
|
||||||
<pie-chart
|
<v-list rounded lines="two"
|
||||||
:items="statisticsData && statisticsData.items && statisticsData.items.length ? statisticsData.items : []"
|
v-if="statisticsData && statisticsData.items && statisticsData.items.length">
|
||||||
:min-valid-percent="0.0001"
|
<template :key="idx"
|
||||||
:show-value="showAmountInChart"
|
v-for="(item, idx) in statisticsData.items">
|
||||||
:enable-click-item="true"
|
<v-list-item class="pl-0" v-if="!item.hidden">
|
||||||
:default-currency="defaultCurrency"
|
<template #prepend>
|
||||||
id-field="id"
|
<router-link class="statistics-list-item" :to="getItemLinkUrl(item)">
|
||||||
name-field="name"
|
<ItemIcon :icon-type="queryChartDataCategory" size="34px"
|
||||||
value-field="totalAmount"
|
:icon-id="item.icon"
|
||||||
percent-field="percent"
|
:color="item.color"></ItemIcon>
|
||||||
currency-field="currency"
|
</router-link>
|
||||||
hidden-field="hidden"
|
</template>
|
||||||
@click="clickPieChartItem"
|
|
||||||
/>
|
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-text :class="{ 'readonly': loading }" v-if="!initing && query.chartType === allChartTypes.Bar">
|
|
||||||
<v-list rounded lines="two"
|
|
||||||
v-if="statisticsData && statisticsData.items && statisticsData.items.length">
|
|
||||||
<template :key="idx"
|
|
||||||
v-for="(item, idx) in statisticsData.items">
|
|
||||||
<v-list-item class="pl-0" v-if="!item.hidden">
|
|
||||||
<template #prepend>
|
|
||||||
<router-link class="statistics-list-item" :to="getItemLinkUrl(item)">
|
<router-link class="statistics-list-item" :to="getItemLinkUrl(item)">
|
||||||
<ItemIcon :icon-type="queryChartDataCategory" size="34px"
|
<div class="d-flex flex-column ml-2">
|
||||||
:icon-id="item.icon"
|
<div class="d-flex">
|
||||||
:color="item.color"></ItemIcon>
|
<span>{{ item.name }}</span>
|
||||||
|
<small class="statistics-percent" v-if="item.percent >= 0">{{ getDisplayPercent(item.percent, 2, '<0.01') }}</small>
|
||||||
|
<v-spacer/>
|
||||||
|
<span class="statistics-amount">{{ getDisplayAmount(item.totalAmount, (item.currency || defaultCurrency)) }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<v-progress-linear :color="item.color ? '#' + item.color : 'primary'"
|
||||||
|
:model-value="item.percent >= 0 ? item.percent : 0"
|
||||||
|
:height="4"></v-progress-linear>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</v-list-item>
|
||||||
<router-link class="statistics-list-item" :to="getItemLinkUrl(item)">
|
<v-divider v-if="!item.hidden && idx !== statisticsData.items.length - 1"/>
|
||||||
<div class="d-flex flex-column ml-2">
|
</template>
|
||||||
<div class="d-flex">
|
</v-list>
|
||||||
<span>{{ item.name }}</span>
|
</v-card-text>
|
||||||
<small class="statistics-percent" v-if="item.percent >= 0">{{ getDisplayPercent(item.percent, 2, '<0.01') }}</small>
|
</v-card>
|
||||||
<v-spacer/>
|
</v-window-item>
|
||||||
<span class="statistics-amount">{{ getDisplayAmount(item.totalAmount, (item.currency || defaultCurrency)) }}</span>
|
</v-window>
|
||||||
</div>
|
</v-main>
|
||||||
<div>
|
</v-layout>
|
||||||
<v-progress-linear :color="item.color ? '#' + item.color : 'primary'"
|
|
||||||
:model-value="item.percent >= 0 ? item.percent : 0"
|
|
||||||
:height="4"></v-progress-linear>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</router-link>
|
|
||||||
</v-list-item>
|
|
||||||
<v-divider v-if="!item.hidden && idx !== statisticsData.items.length - 1"/>
|
|
||||||
</template>
|
|
||||||
</v-list>
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</v-window-item>
|
|
||||||
</v-window>
|
|
||||||
</div>
|
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -218,6 +211,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { useDisplay } from 'vuetify';
|
||||||
|
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useSettingsStore } from '@/stores/setting.js';
|
import { useSettingsStore } from '@/stores/setting.js';
|
||||||
import { useUserStore } from '@/stores/user.js';
|
import { useUserStore } from '@/stores/user.js';
|
||||||
@@ -239,6 +234,7 @@ import {
|
|||||||
mdiArrowRight,
|
mdiArrowRight,
|
||||||
mdiSort,
|
mdiSort,
|
||||||
mdiRefresh,
|
mdiRefresh,
|
||||||
|
mdiMenu,
|
||||||
mdiFilterOutline,
|
mdiFilterOutline,
|
||||||
mdiFilterCogOutline,
|
mdiFilterCogOutline,
|
||||||
mdiPencilOutline,
|
mdiPencilOutline,
|
||||||
@@ -254,10 +250,14 @@ export default {
|
|||||||
CategoryFilterSettingsCard
|
CategoryFilterSettingsCard
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
const { mdAndUp } = useDisplay();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activeTab: 'statisticsPage',
|
activeTab: 'statisticsPage',
|
||||||
initing: true,
|
initing: true,
|
||||||
loading: true,
|
loading: true,
|
||||||
|
alwaysShowNav: mdAndUp.value,
|
||||||
|
showNav: mdAndUp.value,
|
||||||
showCustomDateRangeDialog: false,
|
showCustomDateRangeDialog: false,
|
||||||
showFilterAccountDialog: false,
|
showFilterAccountDialog: false,
|
||||||
showFilterCategoryDialog: false,
|
showFilterCategoryDialog: false,
|
||||||
@@ -267,6 +267,7 @@ export default {
|
|||||||
right: mdiArrowRight,
|
right: mdiArrowRight,
|
||||||
sort: mdiSort,
|
sort: mdiSort,
|
||||||
refresh: mdiRefresh,
|
refresh: mdiRefresh,
|
||||||
|
menu: mdiMenu,
|
||||||
filter: mdiFilterOutline,
|
filter: mdiFilterOutline,
|
||||||
filterSettings: mdiFilterCogOutline,
|
filterSettings: mdiFilterCogOutline,
|
||||||
pencil: mdiPencilOutline,
|
pencil: mdiPencilOutline,
|
||||||
@@ -360,6 +361,13 @@ export default {
|
|||||||
this.statisticsStore.updateTransactionStatisticsFilter({
|
this.statisticsStore.updateTransactionStatisticsFilter({
|
||||||
chartDataType: newValue
|
chartDataType: newValue
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
'display.mdAndUp.value': function (newValue) {
|
||||||
|
this.alwaysShowNav = newValue;
|
||||||
|
|
||||||
|
if (!this.showNav) {
|
||||||
|
this.showNav = newValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -386,6 +394,13 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
setup() {
|
||||||
|
const display = useDisplay();
|
||||||
|
|
||||||
|
return {
|
||||||
|
display: display
|
||||||
|
};
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
reload(force) {
|
reload(force) {
|
||||||
const self = this;
|
const self = this;
|
||||||
@@ -537,11 +552,6 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.statistics-title {
|
|
||||||
overflow-x: auto;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statistics-custom-datetime-range {
|
.statistics-custom-datetime-range {
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
color: rgba(var(--v-theme-on-background), var(--v-medium-emphasis-opacity)) !important;
|
color: rgba(var(--v-theme-on-background), var(--v-medium-emphasis-opacity)) !important;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-card>
|
<v-card>
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="d-flex align-center">
|
<div class="title-and-toolbar d-flex align-center">
|
||||||
<span>{{ $t('Transaction Tags') }}</span>
|
<span>{{ $t('Transaction Tags') }}</span>
|
||||||
<v-btn class="ml-3" color="default" variant="outlined"
|
<v-btn class="ml-3" color="default" variant="outlined"
|
||||||
:disabled="loading || updating || hasEditingTag" @click="add">{{ $t('Add') }}</v-btn>
|
:disabled="loading || updating || hasEditingTag" @click="add">{{ $t('Add') }}</v-btn>
|
||||||
|
|||||||
@@ -2,321 +2,307 @@
|
|||||||
<v-row class="match-height">
|
<v-row class="match-height">
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<v-card>
|
<v-card>
|
||||||
<div class="d-flex flex-column flex-md-row">
|
<v-layout>
|
||||||
<div>
|
<v-navigation-drawer :permanent="alwaysShowNav" v-model="showNav">
|
||||||
<div class="mx-6 my-4">
|
<div class="mx-6 my-4">
|
||||||
<div class="transaction-type-buttons d-flex flex-column">
|
<btn-vertical-group :disabled="loading" :buttons="[
|
||||||
<v-btn border :color="query.type === 0 ? 'primary' : 'default'"
|
{ name: $t('All Types'), value: 0 },
|
||||||
:variant="query.type === 0 ? 'tonal' : 'outlined'" :disabled="loading"
|
{ name: $t('Modify Balance'), value: 1 },
|
||||||
@click="changeTypeFilter(0)">
|
{ name: $t('Income'), value: 2 },
|
||||||
{{ $t('All Types') }}
|
{ name: $t('Expense'), value: 3 },
|
||||||
</v-btn>
|
{ name: $t('Transfer'), value: 4 }
|
||||||
<v-btn border :color="query.type === 1 ? 'primary' : 'default'"
|
]" v-model="query.type" @update:modelValue="changeTypeFilter" />
|
||||||
:variant="query.type === 1 ? 'tonal' : 'outlined'" :disabled="loading"
|
|
||||||
@click="changeTypeFilter(1)">
|
|
||||||
{{ $t('Modify Balance') }}
|
|
||||||
</v-btn>
|
|
||||||
<v-btn border :color="query.type === 2 ? 'primary' : 'default'"
|
|
||||||
:variant="query.type === 2 ? 'tonal' : 'outlined'" :disabled="loading"
|
|
||||||
@click="changeTypeFilter(2)">
|
|
||||||
{{ $t('Income') }}
|
|
||||||
</v-btn>
|
|
||||||
<v-btn border :color="query.type === 3 ? 'primary' : 'default'"
|
|
||||||
:variant="query.type === 3 ? 'tonal' : 'outlined'" :disabled="loading"
|
|
||||||
@click="changeTypeFilter(3)">
|
|
||||||
{{ $t('Expense') }}
|
|
||||||
</v-btn>
|
|
||||||
<v-btn border :color="query.type === 4 ? 'primary' : 'default'"
|
|
||||||
:variant="query.type === 4 ? 'tonal' : 'outlined'" :disabled="loading"
|
|
||||||
@click="changeTypeFilter(4)">
|
|
||||||
{{ $t('Transfer') }}
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<v-divider />
|
<v-divider />
|
||||||
<v-tabs show-arrows class="my-4" direction="vertical"
|
<v-tabs show-arrows class="my-4" direction="vertical"
|
||||||
:disabled="loading" v-model="recentDateRangeType">
|
:disabled="loading" v-model="recentDateRangeType">
|
||||||
<v-tab :key="idx" :value="idx" v-for="(recentDateRange, idx) in recentMonthDateRanges"
|
<v-tab class="tab-text-truncate" :key="idx" :value="idx" v-for="(recentDateRange, idx) in recentMonthDateRanges"
|
||||||
@click="changeDateFilter(recentDateRange)">
|
@click="changeDateFilter(recentDateRange)">
|
||||||
{{ recentDateRange.displayName }}
|
<span class="text-truncate">{{ recentDateRange.displayName }}</span>
|
||||||
</v-tab>
|
</v-tab>
|
||||||
</v-tabs>
|
</v-tabs>
|
||||||
</div>
|
</v-navigation-drawer>
|
||||||
<v-window class="d-flex flex-grow-1 ml-md-5 disable-tab-transition w-100-window-container" v-model="activeTab">
|
<v-main>
|
||||||
<v-window-item value="transactionPage">
|
<v-window class="d-flex flex-grow-1 disable-tab-transition w-100-window-container" v-model="activeTab">
|
||||||
<v-card variant="flat">
|
<v-window-item value="transactionPage">
|
||||||
<template #title>
|
<v-card variant="flat" min-height="830">
|
||||||
<div class="transaction-list-title d-flex align-center text-no-wrap">
|
<template #title>
|
||||||
<span>{{ $t('Transaction List') }}</span>
|
<div class="title-and-toolbar d-flex align-center text-no-wrap">
|
||||||
<v-btn class="ml-3" color="default" variant="outlined"
|
<v-btn class="mr-3 d-md-none" density="compact" color="default" variant="plain"
|
||||||
:disabled="loading || !canAddTransaction" @click="add">{{ $t('Add') }}</v-btn>
|
:ripple="false" :icon="true" @click="showNav = !showNav">
|
||||||
<v-btn density="compact" color="default" variant="text"
|
<v-icon :icon="icons.menu" size="24" />
|
||||||
class="ml-2" :icon="true" :disabled="loading"
|
</v-btn>
|
||||||
v-if="!loading" @click="reload">
|
<span>{{ $t('Transaction List') }}</span>
|
||||||
<v-icon :icon="icons.refresh" size="24" />
|
<v-btn class="ml-3" color="default" variant="outlined"
|
||||||
<v-tooltip activator="parent">{{ $t('Refresh') }}</v-tooltip>
|
:disabled="loading || !canAddTransaction" @click="add">{{ $t('Add') }}</v-btn>
|
||||||
</v-btn>
|
<v-btn density="compact" color="default" variant="text"
|
||||||
<v-progress-circular indeterminate size="24" class="ml-2" v-if="loading"></v-progress-circular>
|
class="ml-2" :icon="true" :disabled="loading"
|
||||||
<v-spacer/>
|
v-if="!loading" @click="reload">
|
||||||
<div class="transaction-keyword-filter ml-2">
|
<v-icon :icon="icons.refresh" size="24" />
|
||||||
<v-text-field density="compact" :disabled="loading"
|
<v-tooltip activator="parent">{{ $t('Refresh') }}</v-tooltip>
|
||||||
:prepend-inner-icon="icons.search"
|
</v-btn>
|
||||||
:append-inner-icon="searchKeyword !== query.keyword ? icons.check : null"
|
<v-progress-circular indeterminate size="24" class="ml-2" v-if="loading"></v-progress-circular>
|
||||||
:placeholder="$t('Search transaction description')"
|
<v-spacer/>
|
||||||
v-model="searchKeyword"
|
<div class="transaction-keyword-filter ml-2">
|
||||||
@click:append-inner="changeKeywordFilter(searchKeyword)"
|
<v-text-field density="compact" :disabled="loading"
|
||||||
@keyup.enter="changeKeywordFilter(searchKeyword)"
|
:prepend-inner-icon="icons.search"
|
||||||
/>
|
:append-inner-icon="searchKeyword !== query.keyword ? icons.check : null"
|
||||||
|
:placeholder="$t('Search transaction description')"
|
||||||
|
v-model="searchKeyword"
|
||||||
|
@click:append-inner="changeKeywordFilter(searchKeyword)"
|
||||||
|
@keyup.enter="changeKeywordFilter(searchKeyword)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</template>
|
|
||||||
|
|
||||||
<v-card-text class="pt-0">
|
<v-card-text class="pt-0">
|
||||||
<div class="transaction-list-datetime-range d-flex align-center">
|
<div class="transaction-list-datetime-range d-flex align-center">
|
||||||
<span class="text-body-1">{{ $t('Date Range') }}</span>
|
<span class="text-body-1">{{ $t('Date Range') }}</span>
|
||||||
<span class="text-body-1 transaction-list-datetime-range-text ml-2">
|
<span class="text-body-1 transaction-list-datetime-range-text ml-2">
|
||||||
<span v-if="!this.query.minTime && !this.query.maxTime">{{ $t('All') }}</span>
|
<span class="text-sm" v-if="!this.query.minTime && !this.query.maxTime">{{ $t('All') }}</span>
|
||||||
<span v-else-if="this.query.minTime || this.query.maxTime">{{ `${queryMinTime} - ${queryMaxTime}` }}</span>
|
<span class="text-sm" v-else-if="this.query.minTime || this.query.maxTime">{{ `${queryMinTime} - ${queryMaxTime}` }}</span>
|
||||||
</span>
|
|
||||||
<v-spacer/>
|
|
||||||
<div class="transaction-list-total-amount-text d-flex align-center" v-if="showTotalAmountInTransactionListPage && currentMonthTotalAmount">
|
|
||||||
<span class="ml-2 text-subtitle-1">{{ $t('Total Income') }}</span>
|
|
||||||
<span class="text-income ml-2" v-if="loading">
|
|
||||||
<v-skeleton-loader type="text" style="width: 60px" :loading="true"></v-skeleton-loader>
|
|
||||||
</span>
|
|
||||||
<span class="text-income ml-2" v-else-if="!loading">
|
|
||||||
{{ currentMonthTotalAmount.income }}
|
|
||||||
</span>
|
|
||||||
<span class="text-subtitle-1 ml-3">{{ $t('Total Expense') }}</span>
|
|
||||||
<span class="text-income ml-2" v-if="loading">
|
|
||||||
<v-skeleton-loader type="text" style="width: 60px" :loading="true"></v-skeleton-loader>
|
|
||||||
</span>
|
|
||||||
<span class="text-expense ml-2" v-else-if="!loading">
|
|
||||||
{{ currentMonthTotalAmount.expense }}
|
|
||||||
</span>
|
</span>
|
||||||
|
<v-spacer/>
|
||||||
|
<div class="skeleton-no-margin d-flex align-center" v-if="showTotalAmountInTransactionListPage && currentMonthTotalAmount">
|
||||||
|
<span class="ml-2 text-subtitle-1">{{ $t('Total Income') }}</span>
|
||||||
|
<span class="text-income ml-2" v-if="loading">
|
||||||
|
<v-skeleton-loader type="text" style="width: 60px" :loading="true"></v-skeleton-loader>
|
||||||
|
</span>
|
||||||
|
<span class="text-income ml-2" v-else-if="!loading">
|
||||||
|
{{ currentMonthTotalAmount.income }}
|
||||||
|
</span>
|
||||||
|
<span class="text-subtitle-1 ml-3">{{ $t('Total Expense') }}</span>
|
||||||
|
<span class="text-income ml-2" v-if="loading">
|
||||||
|
<v-skeleton-loader type="text" style="width: 60px" :loading="true"></v-skeleton-loader>
|
||||||
|
</span>
|
||||||
|
<span class="text-expense ml-2" v-else-if="!loading">
|
||||||
|
{{ currentMonthTotalAmount.expense }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</v-card-text>
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-table class="transaction-table" :hover="!loading">
|
<v-table class="transaction-table" :hover="!loading">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="transaction-table-column-time text-uppercase">{{ $t('Time') }}</th>
|
<th class="transaction-table-column-time text-uppercase">{{ $t('Time') }}</th>
|
||||||
<th class="transaction-table-column-category text-uppercase">
|
<th class="transaction-table-column-category text-uppercase">
|
||||||
<v-menu ref="categoryFilterMenu" class="transaction-category-menu"
|
<v-menu ref="categoryFilterMenu" class="transaction-category-menu"
|
||||||
eager location="bottom" max-height="500"
|
eager location="bottom" max-height="500"
|
||||||
:disabled="query.type === 1"
|
:disabled="query.type === 1"
|
||||||
:close-on-content-click="false"
|
:close-on-content-click="false"
|
||||||
@update:model-value="scrollCategoryMenuToSelectedItem">
|
@update:model-value="scrollCategoryMenuToSelectedItem">
|
||||||
<template #activator="{ props }">
|
<template #activator="{ props }">
|
||||||
<div class="d-flex align-center"
|
<div class="d-flex align-center"
|
||||||
:class="{ 'readonly': loading, 'cursor-pointer': query.type !== 1, 'text-primary': query.categoryId > 0 }" v-bind="props">
|
:class="{ 'readonly': loading, 'cursor-pointer': query.type !== 1, 'text-primary': query.categoryId > 0 }" v-bind="props">
|
||||||
<span>{{ queryCategoryName }}</span>
|
<span>{{ queryCategoryName }}</span>
|
||||||
<v-icon :icon="icons.dropdownMenu" v-show="query.type !== 1" />
|
<v-icon :icon="icons.dropdownMenu" v-show="query.type !== 1" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
<v-list :selected="[query.categoryId]">
|
|
||||||
<v-list-item key="0" value="0" class="text-sm" density="compact"
|
|
||||||
:class="{ 'list-item-selected': query.categoryId === '0' }"
|
|
||||||
:append-icon="(query.categoryId === '0' ? icons.check : null)">
|
|
||||||
<v-list-item-title class="cursor-pointer"
|
|
||||||
@click="changeCategoryFilter('0')">
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<template :key="categoryType"
|
|
||||||
v-for="(categories, categoryType) in allPrimaryCategories">
|
|
||||||
<v-list-item density="compact">
|
|
||||||
<v-list-item-title>
|
|
||||||
<span class="text-sm">{{ getTransactionTypeName(getTransactionTypeFromCategoryType(categoryType), 'Type') }}</span>
|
|
||||||
</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
|
|
||||||
<v-list-group :key="category.id" v-for="category in categories">
|
|
||||||
<template #activator="{ props }" v-if="!category.hidden">
|
|
||||||
<v-divider />
|
|
||||||
<v-list-item class="text-sm" density="compact"
|
|
||||||
:class="getCategoryListItemCheckedClass(category, query.categoryId)"
|
|
||||||
v-bind="props">
|
|
||||||
<v-list-item-title>
|
|
||||||
<div class="d-flex align-center">
|
|
||||||
<ItemIcon icon-type="category" size="24px" :icon-id="category.icon" :color="category.color"></ItemIcon>
|
|
||||||
<span class="text-sm ml-3">{{ category.name }}</span>
|
|
||||||
</div>
|
|
||||||
</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<v-divider />
|
|
||||||
<v-list-item class="text-sm" density="compact"
|
|
||||||
:value="category.id"
|
|
||||||
:append-icon="(query.categoryId === category.id ? icons.check : null)">
|
|
||||||
<v-list-item-title class="cursor-pointer"
|
|
||||||
@click="changeCategoryFilter(category.id)">
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<template :key="subCategory.id"
|
|
||||||
v-for="subCategory in category.subCategories">
|
|
||||||
<v-divider v-if="!subCategory.hidden" />
|
|
||||||
<v-list-item class="text-sm" density="compact"
|
|
||||||
:value="subCategory.id"
|
|
||||||
:class="{ 'list-item-selected': query.categoryId === subCategory.id }"
|
|
||||||
:append-icon="(query.categoryId === subCategory.id ? icons.check : null)"
|
|
||||||
v-if="!subCategory.hidden">
|
|
||||||
<v-list-item-title class="cursor-pointer"
|
|
||||||
@click="changeCategoryFilter(subCategory.id)">
|
|
||||||
<div class="d-flex align-center">
|
|
||||||
<ItemIcon icon-type="category" size="24px" :icon-id="subCategory.icon" :color="subCategory.color"></ItemIcon>
|
|
||||||
<span class="text-sm ml-3">{{ subCategory.name }}</span>
|
|
||||||
</div>
|
|
||||||
</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
</template>
|
|
||||||
</v-list-group>
|
|
||||||
</template>
|
</template>
|
||||||
</v-list>
|
<v-list :selected="[query.categoryId]">
|
||||||
</v-menu>
|
<v-list-item key="0" value="0" class="text-sm" density="compact"
|
||||||
</th>
|
:class="{ 'list-item-selected': query.categoryId === '0' }"
|
||||||
<th class="transaction-table-column-amount text-uppercase">{{ $t('Amount') }}</th>
|
:append-icon="(query.categoryId === '0' ? icons.check : null)">
|
||||||
<th class="transaction-table-column-account text-uppercase">
|
|
||||||
<v-menu ref="accountFilterMenu" class="transaction-account-menu"
|
|
||||||
eager location="bottom" max-height="500"
|
|
||||||
@update:model-value="scrollAccountMenuToSelectedItem">
|
|
||||||
<template #activator="{ props }">
|
|
||||||
<div class="d-flex align-center cursor-pointer"
|
|
||||||
:class="{ 'readonly': loading, 'text-primary': query.accountId > 0 }" v-bind="props">
|
|
||||||
<span>{{ queryAccountName }}</span>
|
|
||||||
<v-icon :icon="icons.dropdownMenu" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<v-list :selected="[query.accountId]">
|
|
||||||
<v-list-item key="0" value="0" class="text-sm" density="compact"
|
|
||||||
:class="{ 'list-item-selected': query.accountId === '0' }"
|
|
||||||
:append-icon="(query.accountId === '0' ? icons.check : null)">
|
|
||||||
<v-list-item-title class="cursor-pointer"
|
|
||||||
@click="changeAccountFilter('0')">
|
|
||||||
<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>
|
|
||||||
<template :key="account.id"
|
|
||||||
v-for="account in allAccounts">
|
|
||||||
<v-divider v-if="!account.hidden" />
|
|
||||||
<v-list-item class="text-sm" density="compact"
|
|
||||||
:value="account.id"
|
|
||||||
:class="{ 'list-item-selected': query.accountId === account.id }"
|
|
||||||
:append-icon="(query.accountId === account.id ? icons.check : null)"
|
|
||||||
v-if="!account.hidden">
|
|
||||||
<v-list-item-title class="cursor-pointer"
|
<v-list-item-title class="cursor-pointer"
|
||||||
@click="changeAccountFilter(account.id)">
|
@click="changeCategoryFilter('0')">
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">
|
||||||
<ItemIcon icon-type="account" size="24px" :icon-id="account.icon" :color="account.color"></ItemIcon>
|
<v-icon :icon="icons.all" />
|
||||||
<span class="text-sm ml-3">{{ account.name }}</span>
|
<span class="text-sm ml-3">{{ $t('All') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
|
|
||||||
|
<template :key="categoryType"
|
||||||
|
v-for="(categories, categoryType) in allPrimaryCategories">
|
||||||
|
<v-list-item density="compact">
|
||||||
|
<v-list-item-title>
|
||||||
|
<span class="text-sm">{{ getTransactionTypeName(getTransactionTypeFromCategoryType(categoryType), 'Type') }}</span>
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-group :key="category.id" v-for="category in categories">
|
||||||
|
<template #activator="{ props }" v-if="!category.hidden">
|
||||||
|
<v-divider />
|
||||||
|
<v-list-item class="text-sm" density="compact"
|
||||||
|
:class="getCategoryListItemCheckedClass(category, query.categoryId)"
|
||||||
|
v-bind="props">
|
||||||
|
<v-list-item-title>
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<ItemIcon icon-type="category" size="24px" :icon-id="category.icon" :color="category.color"></ItemIcon>
|
||||||
|
<span class="text-sm ml-3">{{ category.name }}</span>
|
||||||
|
</div>
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<v-divider />
|
||||||
|
<v-list-item class="text-sm" density="compact"
|
||||||
|
:value="category.id"
|
||||||
|
:append-icon="(query.categoryId === category.id ? icons.check : null)">
|
||||||
|
<v-list-item-title class="cursor-pointer"
|
||||||
|
@click="changeCategoryFilter(category.id)">
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<template :key="subCategory.id"
|
||||||
|
v-for="subCategory in category.subCategories">
|
||||||
|
<v-divider v-if="!subCategory.hidden" />
|
||||||
|
<v-list-item class="text-sm" density="compact"
|
||||||
|
:value="subCategory.id"
|
||||||
|
:class="{ 'list-item-selected': query.categoryId === subCategory.id }"
|
||||||
|
:append-icon="(query.categoryId === subCategory.id ? icons.check : null)"
|
||||||
|
v-if="!subCategory.hidden">
|
||||||
|
<v-list-item-title class="cursor-pointer"
|
||||||
|
@click="changeCategoryFilter(subCategory.id)">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<ItemIcon icon-type="category" size="24px" :icon-id="subCategory.icon" :color="subCategory.color"></ItemIcon>
|
||||||
|
<span class="text-sm ml-3">{{ subCategory.name }}</span>
|
||||||
|
</div>
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
|
</v-list-group>
|
||||||
|
</template>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</th>
|
||||||
|
<th class="transaction-table-column-amount text-uppercase">{{ $t('Amount') }}</th>
|
||||||
|
<th class="transaction-table-column-account text-uppercase">
|
||||||
|
<v-menu ref="accountFilterMenu" class="transaction-account-menu"
|
||||||
|
eager location="bottom" max-height="500"
|
||||||
|
@update:model-value="scrollAccountMenuToSelectedItem">
|
||||||
|
<template #activator="{ props }">
|
||||||
|
<div class="d-flex align-center cursor-pointer"
|
||||||
|
:class="{ 'readonly': loading, 'text-primary': query.accountId > 0 }" v-bind="props">
|
||||||
|
<span>{{ queryAccountName }}</span>
|
||||||
|
<v-icon :icon="icons.dropdownMenu" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</v-list>
|
<v-list :selected="[query.accountId]">
|
||||||
</v-menu>
|
<v-list-item key="0" value="0" class="text-sm" density="compact"
|
||||||
</th>
|
:class="{ 'list-item-selected': query.accountId === '0' }"
|
||||||
<th class="transaction-table-column-description text-uppercase">{{ $t('Description') }}</th>
|
:append-icon="(query.accountId === '0' ? icons.check : null)">
|
||||||
</tr>
|
<v-list-item-title class="cursor-pointer"
|
||||||
</thead>
|
@click="changeAccountFilter('0')">
|
||||||
|
<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>
|
||||||
|
<template :key="account.id"
|
||||||
|
v-for="account in allAccounts">
|
||||||
|
<v-divider v-if="!account.hidden" />
|
||||||
|
<v-list-item class="text-sm" density="compact"
|
||||||
|
:value="account.id"
|
||||||
|
:class="{ 'list-item-selected': query.accountId === account.id }"
|
||||||
|
:append-icon="(query.accountId === account.id ? icons.check : null)"
|
||||||
|
v-if="!account.hidden">
|
||||||
|
<v-list-item-title class="cursor-pointer"
|
||||||
|
@click="changeAccountFilter(account.id)">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<ItemIcon icon-type="account" size="24px" :icon-id="account.icon" :color="account.color"></ItemIcon>
|
||||||
|
<span class="text-sm ml-3">{{ account.name }}</span>
|
||||||
|
</div>
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</th>
|
||||||
|
<th class="transaction-table-column-description text-uppercase">{{ $t('Description') }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
<tbody v-if="loading && (!transactions || !transactions.length || transactions.length < 1)">
|
<tbody v-if="loading && (!transactions || !transactions.length || transactions.length < 1)">
|
||||||
<tr :key="itemIdx" v-for="itemIdx in [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]">
|
<tr :key="itemIdx" v-for="itemIdx in [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]">
|
||||||
<td class="px-0" colspan="5">
|
<td class="px-0" colspan="5">
|
||||||
<v-skeleton-loader type="text" :loading="true"></v-skeleton-loader>
|
<v-skeleton-loader type="text" :loading="true"></v-skeleton-loader>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
|
|
||||||
<tbody v-if="!loading && (!transactions || !transactions.length || transactions.length < 1)">
|
|
||||||
<tr>
|
|
||||||
<td colspan="5">{{ $t('No transaction data') }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
|
|
||||||
<tbody :key="transaction.id"
|
|
||||||
:class="{ 'disabled': loading, 'has-bottom-border': idx < transactions.length - 1 }"
|
|
||||||
v-for="(transaction, idx) in transactions">
|
|
||||||
<tr class="transaction-list-row-date no-hover text-sm"
|
|
||||||
v-if="idx === 0 || (idx > 0 && (transaction.date !== transactions[idx - 1].date))">
|
|
||||||
<td colspan="5" class="font-weight-bold">
|
|
||||||
<div class="d-flex align-center">
|
|
||||||
<span>{{ getLongDate(transaction) }}</span>
|
|
||||||
<v-chip class="ml-1" color="default" size="x-small">
|
|
||||||
{{ getWeekdayLongName(transaction) }}
|
|
||||||
</v-chip>
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="transaction-table-row-data text-sm cursor-pointer"
|
</tbody>
|
||||||
@click="show(transaction)">
|
|
||||||
<td class="transaction-table-column-time">
|
|
||||||
<div class="d-flex flex-column">
|
|
||||||
<span>{{ getDisplayTime(transaction) }}</span>
|
|
||||||
<span class="text-caption" v-if="transaction.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimezone(transaction) }}</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="transaction-table-column-category">
|
|
||||||
<div class="d-flex align-center">
|
|
||||||
<ItemIcon size="24px" icon-type="category"
|
|
||||||
:icon-id="transaction.category.icon"
|
|
||||||
:color="transaction.category.color"
|
|
||||||
v-if="transaction.category && transaction.category.color"></ItemIcon>
|
|
||||||
<v-icon size="24" :icon="icons.modifyBalance" v-else-if="!transaction.category || !transaction.category.color" />
|
|
||||||
<span class="ml-2" v-if="transaction.type === allTransactionTypes.ModifyBalance">
|
|
||||||
{{ $t('Modify Balance') }}
|
|
||||||
</span>
|
|
||||||
<span class="ml-2" v-else-if="transaction.type !== allTransactionTypes.ModifyBalance && transaction.category">
|
|
||||||
{{ transaction.category.name }}
|
|
||||||
</span>
|
|
||||||
<span class="ml-2" v-else-if="transaction.type !== allTransactionTypes.ModifyBalance && !transaction.category">
|
|
||||||
{{ getTransactionTypeName(transaction.type, 'Transaction') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="transaction-table-column-amount" :class="{ 'text-expense': transaction.type === allTransactionTypes.Expense, 'text-income': transaction.type === allTransactionTypes.Income }">
|
|
||||||
<div v-if="transaction.sourceAccount">
|
|
||||||
<span v-if="!query.accountId || query.accountId === '0' || (transaction.sourceAccount && (transaction.sourceAccount.id === query.accountId || transaction.sourceAccount.parentId === query.accountId))">{{ getDisplayAmount(transaction.sourceAmount, transaction.sourceAccount.currency, transaction.hideAmount) }}</span>
|
|
||||||
<span v-else-if="query.accountId && query.accountId !== '0' && transaction.destinationAccount && (transaction.destinationAccount.id === query.accountId || transaction.destinationAccount.parentId === query.accountId)">{{ getDisplayAmount(transaction.destinationAmount, transaction.destinationAccount.currency, transaction.hideAmount) }}</span>
|
|
||||||
<span v-else></span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="transaction-table-column-account">
|
|
||||||
<div class="d-flex align-center">
|
|
||||||
<span v-if="transaction.sourceAccount">{{ transaction.sourceAccount.name }}</span>
|
|
||||||
<v-icon class="mx-1" size="13" :icon="icons.arrowRight" v-if="transaction.sourceAccount && transaction.type === allTransactionTypes.Transfer && transaction.destinationAccount && transaction.sourceAccount.id !== transaction.destinationAccount.id"></v-icon>
|
|
||||||
<span v-if="transaction.sourceAccount && transaction.type === allTransactionTypes.Transfer && transaction.destinationAccount && transaction.sourceAccount.id !== transaction.destinationAccount.id">{{ transaction.destinationAccount.name }}</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="transaction-table-column-description text-truncate">
|
|
||||||
{{ transaction.comment }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</v-table>
|
|
||||||
|
|
||||||
<div class="mt-2 mb-4">
|
<tbody v-if="!loading && (!transactions || !transactions.length || transactions.length < 1)">
|
||||||
<v-pagination :total-visible="6" :length="totalPageCount"
|
<tr>
|
||||||
v-model="paginationCurrentPage"></v-pagination>
|
<td colspan="5">{{ $t('No transaction data') }}</td>
|
||||||
</div>
|
</tr>
|
||||||
</v-card>
|
</tbody>
|
||||||
</v-window-item>
|
|
||||||
</v-window>
|
<tbody :key="transaction.id"
|
||||||
</div>
|
:class="{ 'disabled': loading, 'has-bottom-border': idx < transactions.length - 1 }"
|
||||||
|
v-for="(transaction, idx) in transactions">
|
||||||
|
<tr class="transaction-list-row-date no-hover text-sm"
|
||||||
|
v-if="idx === 0 || (idx > 0 && (transaction.date !== transactions[idx - 1].date))">
|
||||||
|
<td colspan="5" class="font-weight-bold">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<span>{{ getLongDate(transaction) }}</span>
|
||||||
|
<v-chip class="ml-1" color="default" size="x-small">
|
||||||
|
{{ getWeekdayLongName(transaction) }}
|
||||||
|
</v-chip>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="transaction-table-row-data text-sm cursor-pointer"
|
||||||
|
@click="show(transaction)">
|
||||||
|
<td class="transaction-table-column-time">
|
||||||
|
<div class="d-flex flex-column">
|
||||||
|
<span>{{ getDisplayTime(transaction) }}</span>
|
||||||
|
<span class="text-caption" v-if="transaction.utcOffset !== currentTimezoneOffsetMinutes">{{ getDisplayTimezone(transaction) }}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="transaction-table-column-category">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<ItemIcon size="24px" icon-type="category"
|
||||||
|
:icon-id="transaction.category.icon"
|
||||||
|
:color="transaction.category.color"
|
||||||
|
v-if="transaction.category && transaction.category.color"></ItemIcon>
|
||||||
|
<v-icon size="24" :icon="icons.modifyBalance" v-else-if="!transaction.category || !transaction.category.color" />
|
||||||
|
<span class="ml-2" v-if="transaction.type === allTransactionTypes.ModifyBalance">
|
||||||
|
{{ $t('Modify Balance') }}
|
||||||
|
</span>
|
||||||
|
<span class="ml-2" v-else-if="transaction.type !== allTransactionTypes.ModifyBalance && transaction.category">
|
||||||
|
{{ transaction.category.name }}
|
||||||
|
</span>
|
||||||
|
<span class="ml-2" v-else-if="transaction.type !== allTransactionTypes.ModifyBalance && !transaction.category">
|
||||||
|
{{ getTransactionTypeName(transaction.type, 'Transaction') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="transaction-table-column-amount" :class="{ 'text-expense': transaction.type === allTransactionTypes.Expense, 'text-income': transaction.type === allTransactionTypes.Income }">
|
||||||
|
<div v-if="transaction.sourceAccount">
|
||||||
|
<span v-if="!query.accountId || query.accountId === '0' || (transaction.sourceAccount && (transaction.sourceAccount.id === query.accountId || transaction.sourceAccount.parentId === query.accountId))">{{ getDisplayAmount(transaction.sourceAmount, transaction.sourceAccount.currency, transaction.hideAmount) }}</span>
|
||||||
|
<span v-else-if="query.accountId && query.accountId !== '0' && transaction.destinationAccount && (transaction.destinationAccount.id === query.accountId || transaction.destinationAccount.parentId === query.accountId)">{{ getDisplayAmount(transaction.destinationAmount, transaction.destinationAccount.currency, transaction.hideAmount) }}</span>
|
||||||
|
<span v-else></span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="transaction-table-column-account">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<span v-if="transaction.sourceAccount">{{ transaction.sourceAccount.name }}</span>
|
||||||
|
<v-icon class="mx-1" size="13" :icon="icons.arrowRight" v-if="transaction.sourceAccount && transaction.type === allTransactionTypes.Transfer && transaction.destinationAccount && transaction.sourceAccount.id !== transaction.destinationAccount.id"></v-icon>
|
||||||
|
<span v-if="transaction.sourceAccount && transaction.type === allTransactionTypes.Transfer && transaction.destinationAccount && transaction.sourceAccount.id !== transaction.destinationAccount.id">{{ transaction.destinationAccount.name }}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="transaction-table-column-description text-truncate">
|
||||||
|
{{ transaction.comment }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</v-table>
|
||||||
|
|
||||||
|
<div class="mt-2 mb-4">
|
||||||
|
<v-pagination :total-visible="6" :length="totalPageCount"
|
||||||
|
v-model="paginationCurrentPage"></v-pagination>
|
||||||
|
</div>
|
||||||
|
</v-card>
|
||||||
|
</v-window-item>
|
||||||
|
</v-window>
|
||||||
|
</v-main>
|
||||||
|
</v-layout>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
@@ -332,6 +318,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { useDisplay } from 'vuetify';
|
||||||
|
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useSettingsStore } from '@/stores/setting.js';
|
import { useSettingsStore } from '@/stores/setting.js';
|
||||||
import { useUserStore } from '@/stores/user.js';
|
import { useUserStore } from '@/stores/user.js';
|
||||||
@@ -370,6 +358,7 @@ import {
|
|||||||
mdiCheck,
|
mdiCheck,
|
||||||
mdiTextBoxCheckOutline,
|
mdiTextBoxCheckOutline,
|
||||||
mdiRefresh,
|
mdiRefresh,
|
||||||
|
mdiMenu,
|
||||||
mdiMenuDown,
|
mdiMenuDown,
|
||||||
mdiPencilBoxOutline,
|
mdiPencilBoxOutline,
|
||||||
mdiArrowRight,
|
mdiArrowRight,
|
||||||
@@ -387,6 +376,8 @@ export default {
|
|||||||
'initAccountId'
|
'initAccountId'
|
||||||
],
|
],
|
||||||
data() {
|
data() {
|
||||||
|
const { mdAndUp } = useDisplay();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
loading: true,
|
loading: true,
|
||||||
updating: false,
|
updating: false,
|
||||||
@@ -396,6 +387,8 @@ export default {
|
|||||||
searchKeyword: '',
|
searchKeyword: '',
|
||||||
currentPageTransactions: [],
|
currentPageTransactions: [],
|
||||||
totalPageCount: 1,
|
totalPageCount: 1,
|
||||||
|
alwaysShowNav: mdAndUp.value,
|
||||||
|
showNav: mdAndUp.value,
|
||||||
showCustomDateRangeDialog: false,
|
showCustomDateRangeDialog: false,
|
||||||
transactionRemoving: {},
|
transactionRemoving: {},
|
||||||
icons: {
|
icons: {
|
||||||
@@ -403,6 +396,7 @@ export default {
|
|||||||
check: mdiCheck,
|
check: mdiCheck,
|
||||||
all: mdiTextBoxCheckOutline,
|
all: mdiTextBoxCheckOutline,
|
||||||
refresh: mdiRefresh,
|
refresh: mdiRefresh,
|
||||||
|
menu: mdiMenu,
|
||||||
dropdownMenu: mdiMenuDown,
|
dropdownMenu: mdiMenuDown,
|
||||||
modifyBalance: mdiPencilBoxOutline,
|
modifyBalance: mdiPencilBoxOutline,
|
||||||
arrowRight: mdiArrowRight,
|
arrowRight: mdiArrowRight,
|
||||||
@@ -577,6 +571,22 @@ export default {
|
|||||||
accountId: this.initAccountId
|
accountId: this.initAccountId
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
setup() {
|
||||||
|
const display = useDisplay();
|
||||||
|
|
||||||
|
return {
|
||||||
|
display: display
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'display.mdAndUp.value': function (newValue) {
|
||||||
|
this.alwaysShowNav = newValue;
|
||||||
|
|
||||||
|
if (!this.showNav) {
|
||||||
|
this.showNav = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
beforeRouteUpdate(to) {
|
beforeRouteUpdate(to) {
|
||||||
if (to.query) {
|
if (to.query) {
|
||||||
this.init({
|
this.init({
|
||||||
@@ -707,10 +717,6 @@ export default {
|
|||||||
this.$router.push(this.getFilterLinkUrl());
|
this.$router.push(this.getFilterLinkUrl());
|
||||||
},
|
},
|
||||||
changeTypeFilter(type) {
|
changeTypeFilter(type) {
|
||||||
if (this.query.type === type) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let removeCategoryFilter = false;
|
let removeCategoryFilter = false;
|
||||||
|
|
||||||
if (type && this.query.categoryId) {
|
if (type && this.query.categoryId) {
|
||||||
@@ -888,42 +894,24 @@ export default {
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.transaction-keyword-filter .v-input--density-compact {
|
.transaction-keyword-filter .v-input--density-compact {
|
||||||
--v-input-control-height: 36px;
|
--v-input-control-height: 36px !important;
|
||||||
--v-input-padding-top: 5px;
|
--v-input-padding-top: 5px !important;
|
||||||
--v-input-padding-bottom: 5px;
|
--v-input-padding-bottom: 5px !important;
|
||||||
|
--v-input-chips-margin-top: 0px !important;
|
||||||
|
--v-input-chips-margin-bottom: 0px !important;
|
||||||
inline-size: 20rem;
|
inline-size: 20rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.transaction-type-buttons .v-btn:not(:first-child) {
|
|
||||||
border-top-left-radius: inherit;
|
|
||||||
border-top-right-radius: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.transaction-type-buttons .v-btn:not(:last-child) {
|
|
||||||
border-bottom: 0;
|
|
||||||
border-bottom-left-radius: inherit;
|
|
||||||
border-bottom-right-radius: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.transaction-list-title {
|
|
||||||
overflow-x: auto;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.transaction-list-datetime-range {
|
.transaction-list-datetime-range {
|
||||||
height: 28px;
|
min-height: 28px;
|
||||||
overflow-x: auto;
|
flex-wrap: wrap;
|
||||||
white-space: nowrap;
|
row-gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.transaction-list-datetime-range .transaction-list-datetime-range-text {
|
.transaction-list-datetime-range .transaction-list-datetime-range-text {
|
||||||
color: rgba(var(--v-theme-on-background), var(--v-medium-emphasis-opacity)) !important;
|
color: rgba(var(--v-theme-on-background), var(--v-medium-emphasis-opacity)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.transaction-list-total-amount-text .v-skeleton-loader__text {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-table.transaction-table .transaction-list-row-date > td {
|
.v-table.transaction-table .transaction-list-row-date > td {
|
||||||
height: 40px !important;
|
height: 40px !important;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user