Files
ezbookkeeping/src/components/mobile/AccountBalanceTrendsBarChart.vue
T
2025-08-30 00:40:33 +08:00

129 lines
4.8 KiB
Vue

<template>
<f7-list class="skeleton-text margin-top-half" media-list v-if="loading">
<f7-list-item class="account-balance-trends-list-item" title="Date Range" after="0.00 USD"
:key="itemIdx" v-for="itemIdx in [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]">
<template #media>
<f7-icon f7="app_fill"></f7-icon>
</template>
<template #inner>
<div class="display-flex padding-top-half">
<div class="statistics-percent-line width-100 no-margin-horizontal">
<f7-progressbar :progress="0"></f7-progressbar>
</div>
</div>
</template>
</f7-list-item>
</f7-list>
<f7-list v-else-if="!loading && (!allVirtualListItems || !allVirtualListItems.length)">
<f7-list-item :title="tt('No transaction data')"></f7-list-item>
</f7-list>
<f7-list class="margin-top-half" media-list virtual-list :virtual-list-params="{ items: allVirtualListItems, renderExternal, height: 'auto' }"
:key="`account-balance-trends-${dateAggregationType}`"
v-else-if="!loading && allVirtualListItems && allVirtualListItems.length > 0">
<ul>
<f7-list-item class="account-balance-trends-list-item"
:key="item.index"
:style="`top: ${virtualDataItems.topPosition}px`"
:virtual-list-index="item.index"
:title="item.displayDate"
:after="formatAmountToLocalizedNumeralsWithCurrency(item.closingBalance, account.currency)"
v-for="item in virtualDataItems.items"
>
<template #media>
<f7-icon f7="calendar"></f7-icon>
</template>
<template #inner>
<div class="display-flex padding-top-half">
<div class="statistics-percent-line width-100 no-margin-horizontal">
<f7-progressbar :progress="item.percent >= 0 ? item.percent : 0" :style="{ '--f7-progressbar-progress-color': item.color } "></f7-progressbar>
</div>
</div>
</template>
</f7-list-item>
</ul>
</f7-list>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { useI18n } from '@/locales/helpers.ts';
import {
type AccountBalanceTrendsChartItem,
type CommonAccountBalanceTrendsChartProps,
useAccountBalanceTrendsChartBase
} from '@/components/base/AccountBalanceTrendsChartBase.ts'
import type { ColorStyleValue } from '@/core/color.ts';
import { DEFAULT_CHART_COLORS } from '@/consts/color.ts';
interface MobileAccountBalanceTrendsChartItem extends AccountBalanceTrendsChartItem {
index: number;
percent: number;
color: ColorStyleValue;
}
interface MobileAccountBalanceTrendsChartProps extends CommonAccountBalanceTrendsChartProps {
loading?: boolean;
}
interface MobileAccountBalanceTrendsChartVirtualListData {
items: MobileAccountBalanceTrendsChartItem[],
topPosition: number
}
const props = defineProps<MobileAccountBalanceTrendsChartProps>();
const { tt, formatAmountToLocalizedNumeralsWithCurrency } = useI18n();
const { allDataItems } = useAccountBalanceTrendsChartBase(props);
const virtualDataItems = ref<MobileAccountBalanceTrendsChartVirtualListData>({
items: [],
topPosition: 0
});
const allVirtualListItems = computed<MobileAccountBalanceTrendsChartItem[]>(() => {
const ret: MobileAccountBalanceTrendsChartItem[] = [];
let maxClosingBalance = 0;
for (let i = 0; i < allDataItems.value.length; i++) {
const dataItem = allDataItems.value[i];
if (dataItem.closingBalance > maxClosingBalance) {
maxClosingBalance = dataItem.closingBalance;
}
const finalDataItem: MobileAccountBalanceTrendsChartItem = {
index: i,
displayDate: dataItem.displayDate,
openingBalance: dataItem.openingBalance,
closingBalance: dataItem.closingBalance,
medianBalance: dataItem.medianBalance,
averageBalance: dataItem.averageBalance,
minimumBalance: dataItem.minimumBalance,
maximumBalance: dataItem.maximumBalance,
color: `#${DEFAULT_CHART_COLORS[0]}`,
percent: 0.0
};
ret.push(finalDataItem);
}
for (let i = 0; i < ret.length; i++) {
if (maxClosingBalance > 0 && ret[i].closingBalance > 0) {
ret[i].percent = 100.0 * ret[i].closingBalance / maxClosingBalance;
} else {
ret[i].percent = 0.0;
}
}
return ret;
});
function renderExternal(vl: unknown, vlData: MobileAccountBalanceTrendsChartVirtualListData): void {
virtualDataItems.value = vlData;
}
</script>