migrate transaction statistics page to composition API and typescript

This commit is contained in:
MaysWind
2025-01-25 14:52:57 +08:00
parent 8207373a05
commit a27a2556aa
7 changed files with 1316 additions and 1272 deletions
+1 -1
View File
@@ -119,7 +119,7 @@ export class ChartDataType implements TypeAndName {
return this.availableAnalysisTypes[analysisType] || false;
}
public static values(analysisType: StatisticsAnalysisType | undefined): ChartDataType[] {
public static values(analysisType?: StatisticsAnalysisType): ChartDataType[] {
if (analysisType === undefined) {
return ChartDataType.allInstances;
}
+12 -2
View File
@@ -1,4 +1,4 @@
import type { TypeAndDisplayName } from '@/core/base.ts';
import type { TypeAndName, TypeAndDisplayName } from '@/core/base.ts';
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
export function isFunction(val: unknown): val is Function {
@@ -37,7 +37,7 @@ export function isBoolean(val: unknown): val is boolean {
return typeof(val) === 'boolean';
}
export function isYearMonth(val: unknown): boolean {
export function isYearMonth(val: unknown): val is string {
if (!isString(val)) {
return false;
}
@@ -312,6 +312,16 @@ export function getItemByKeyValue<T>(src: Record<string, T>[] | Record<string, R
return null;
}
export function findNameByType(items: TypeAndName[], type: number): string | null {
for (const item of items) {
if (item.type === type) {
return item.name;
}
}
return null;
}
export function findDisplayNameByType(items: TypeAndDisplayName[], type: number): string | null {
for (const item of items) {
if (item.type === type) {
+54 -1
View File
@@ -105,13 +105,17 @@ import {
isPM,
formatUnixTime,
formatCurrentTime,
parseDateFromUnixTime,
getYear,
getTimezoneOffset,
getTimezoneOffsetMinutes,
getBrowserTimezoneOffset,
getBrowserTimezoneOffsetMinutes,
getTimeDifferenceHoursAndMinutes,
getDateTimeFormatType,
getRecentMonthDateRanges
getRecentMonthDateRanges,
isDateRangeMatchFullYears,
isDateRangeMatchFullMonths
} from '@/lib/datetime.ts';
import {
@@ -1206,6 +1210,54 @@ export function useI18n() {
}
}
function formatDateRange(dateType: number, startTime: number, endTime: number): string {
if (dateType === DateRange.All.type) {
return t(DateRange.All.name);
}
const allDateRanges = DateRange.values();
for (let i = 0; i < allDateRanges.length; i++) {
const dateRange = allDateRanges[i];
if (dateRange && dateRange.type !== DateRange.Custom.type && dateRange.type === dateType && dateRange.name) {
return t(dateRange.name);
}
}
if (isDateRangeMatchFullYears(startTime, endTime)) {
const format = getLocalizedShortYearFormat();
const displayStartTime = formatUnixTime(startTime, format);
const displayEndTime = formatUnixTime(endTime, format);
return displayStartTime !== displayEndTime ? `${displayStartTime} ~ ${displayEndTime}` : displayStartTime;
}
if (isDateRangeMatchFullMonths(startTime, endTime)) {
const format = getLocalizedShortYearMonthFormat();
const displayStartTime = formatUnixTime(startTime, format);
const displayEndTime = formatUnixTime(endTime, format);
return displayStartTime !== displayEndTime ? `${displayStartTime} ~ ${displayEndTime}` : displayStartTime;
}
const startTimeYear = getYear(parseDateFromUnixTime(startTime));
const endTimeYear = getYear(parseDateFromUnixTime(endTime));
const format = getLocalizedShortDateFormat();
const displayStartTime = formatUnixTime(startTime, format);
const displayEndTime = formatUnixTime(endTime, format);
if (displayStartTime === displayEndTime) {
return displayStartTime;
} else if (startTimeYear === endTimeYear) {
const displayShortEndTime = formatUnixTime(endTime, getLocalizedShortMonthDayFormat());
return `${displayStartTime} ~ ${displayShortEndTime}`;
}
return `${displayStartTime} ~ ${displayEndTime}`;
}
function getTimezoneDifferenceDisplayText(utcOffset: number): string {
const defaultTimezoneOffset = getTimezoneOffsetMinutes();
const offsetTime = getTimeDifferenceHoursAndMinutes(utcOffset - defaultTimezoneOffset);
@@ -1486,6 +1538,7 @@ export function useI18n() {
formatUnixTimeToLongTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongTimeFormat(), utcOffset, currentUtcOffset),
formatUnixTimeToShortTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortTimeFormat(), utcOffset, currentUtcOffset),
formatYearQuarter,
formatDateRange,
getTimezoneDifferenceDisplayText,
appendDigitGroupingSymbol: getNumberWithDigitGroupingSymbol,
parseAmount: getParsedAmountNumber,
+21 -4
View File
@@ -101,7 +101,24 @@ interface WritableTransactioTrendsAnalysisDataItem {
items: TransactionTrendsAnalysisDataAmount[];
}
export interface TransactionStatisticsFilter {
export interface TransactionStatisticsPartialFilter {
chartDataType?: number;
categoricalChartType?: number;
categoricalChartDateType?: number;
categoricalChartStartTime?: number;
categoricalChartEndTime?: number;
trendChartType?: number;
trendChartDateType?: number;
trendChartStartYearMonth?: string;
trendChartEndYearMonth?: string;
filterAccountIds?: Record<string, boolean>;
filterCategoryIds?: Record<string, boolean>;
tagIds?: string;
tagFilterType?: number;
sortingType?: number;
}
export interface TransactionStatisticsFilter extends TransactionStatisticsPartialFilter {
chartDataType: number;
categoricalChartType: number;
categoricalChartDateType: number;
@@ -672,7 +689,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
transactionStatisticsStateInvalid.value = true;
}
function initTransactionStatisticsFilter(analysisType: StatisticsAnalysisType, filter?: TransactionStatisticsFilter): void {
function initTransactionStatisticsFilter(analysisType: StatisticsAnalysisType, filter?: TransactionStatisticsPartialFilter): void {
if (filter && isInteger(filter.chartDataType)) {
transactionStatisticsFilter.value.chartDataType = filter.chartDataType;
} else {
@@ -810,7 +827,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
}
}
function updateTransactionStatisticsFilter(filter: TransactionStatisticsFilter): boolean {
function updateTransactionStatisticsFilter(filter: TransactionStatisticsPartialFilter): boolean {
let changed = false;
if (filter && isInteger(filter.chartDataType) && transactionStatisticsFilter.value.chartDataType !== filter.chartDataType) {
@@ -943,7 +960,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
return querys.join('&');
}
function getTransactionListPageParams(analysisType: StatisticsAnalysisType, itemId: string, dateRange: TimeRangeAndDateType): string {
function getTransactionListPageParams(analysisType: StatisticsAnalysisType, itemId: string, dateRange?: TimeRangeAndDateType): string {
const querys: string[] = [];
if (transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByAccount.type
@@ -0,0 +1,243 @@
import { ref, computed } from 'vue';
import { useI18n } from '@/locales/helpers.ts';
import { useSettingsStore } from '@/stores/setting.ts';
import { useUserStore } from '@/stores/user.ts';
import { type TransactionStatisticsFilter, useStatisticsStore } from '@/stores/statistics.ts';
import type { TypeAndDisplayName } from '@/core/base.ts';
import { type LocalizedDateRange, DateRangeScene, DateRange } from '@/core/datetime.ts';
import { StatisticsAnalysisType, ChartDataType, ChartSortingType, ChartDateAggregationType } from '@/core/statistics.ts';
import type { TransactionCategoricalAnalysisData, TransactionTrendsAnalysisData } from '@/models/transaction.ts';
import { limitText, findNameByType, findDisplayNameByType } from '@/lib/common.ts';
import { getYearMonthFirstUnixTime, getYearMonthLastUnixTime } from '@/lib/datetime.ts';
export function useStatisticsTransactionPageBase() {
const {
tt,
getAllDateRanges,
getAllStatisticsSortingTypes,
getAllStatisticsDateAggregationTypes,
formatUnixTimeToLongDateTime,
formatUnixTimeToLongYearMonth,
formatDateRange,
formatAmountWithCurrency
} = useI18n();
const settingsStore = useSettingsStore();
const userStore = useUserStore();
const statisticsStore = useStatisticsStore();
const loading = ref<boolean>(true);
const analysisType = ref<StatisticsAnalysisType>(StatisticsAnalysisType.CategoricalAnalysis);
const trendDateAggregationType = ref<number>(ChartDateAggregationType.Default.type);
const showAccountBalance = computed<boolean>(() => settingsStore.appSettings.showAccountBalance);
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
const firstDayOfWeek = computed<number>(() => userStore.currentUserFirstDayOfWeek);
const allDateRanges = computed<LocalizedDateRange[]>(() => {
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
return getAllDateRanges(DateRangeScene.Normal, true);
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
return getAllDateRanges(DateRangeScene.TrendAnalysis, true);
} else {
return [];
}
});
const allSortingTypes = computed<TypeAndDisplayName[]>(() => getAllStatisticsSortingTypes());
const allDateAggregationTypes = computed<TypeAndDisplayName[]>(() => getAllStatisticsDateAggregationTypes());
const query = computed<TransactionStatisticsFilter>(() => statisticsStore.transactionStatisticsFilter);
const queryChartDataCategory = computed<string>(() => statisticsStore.categoricalAnalysisChartDataCategory);
const queryDateType = computed<number | null>(() => {
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
return query.value.categoricalChartDateType;
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
return query.value.trendChartDateType;
} else {
return null;
}
});
const queryStartTime = computed<string>(() => {
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
return formatUnixTimeToLongDateTime(query.value.categoricalChartStartTime);
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
return formatUnixTimeToLongYearMonth(getYearMonthFirstUnixTime(query.value.trendChartStartYearMonth));
} else {
return '';
}
});
const queryEndTime = computed<string>(() => {
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
return formatUnixTimeToLongDateTime(query.value.categoricalChartEndTime);
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
return formatUnixTimeToLongYearMonth(getYearMonthLastUnixTime(query.value.trendChartEndYearMonth));
} else {
return '';
}
});
const queryDateRangeName = computed<string>(() => {
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type ||
query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
return tt(DateRange.All.name);
}
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
return formatDateRange(query.value.categoricalChartDateType, query.value.categoricalChartStartTime, query.value.categoricalChartEndTime);
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
return formatDateRange(query.value.trendChartDateType, getYearMonthFirstUnixTime(query.value.trendChartStartYearMonth), getYearMonthLastUnixTime(query.value.trendChartEndYearMonth));
} else {
return '';
}
});
const queryChartDataTypeName = computed<string>(() => {
const queryChartDataTypeName = findNameByType(ChartDataType.values(), query.value.chartDataType) || 'Statistics';
return tt(queryChartDataTypeName);
});
const querySortingTypeName = computed<string>(() => {
const querySortingTypeName = findNameByType(ChartSortingType.values(), query.value.sortingType) || 'System Default';
return tt(querySortingTypeName);
});
const queryTrendDateAggregationTypeName = computed<string>(() => findDisplayNameByType(allDateAggregationTypes.value, trendDateAggregationType.value) || '');
const isQueryDateRangeChanged = computed<boolean>(() => {
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type ||
query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
return false;
}
return !!query.value.categoricalChartStartTime || !!query.value.categoricalChartEndTime;
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
return !!query.value.trendChartStartYearMonth || !!query.value.trendChartEndYearMonth;
} else {
return false;
}
});
const canShiftDateRange = computed<boolean>(() => {
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
return false;
}
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
return query.value.categoricalChartDateType !== DateRange.All.type;
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
return query.value.trendChartDateType !== DateRange.All.type;
} else {
return false;
}
});
const showCustomDateRange = computed<boolean>(() => {
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
return query.value.categoricalChartDateType === DateRange.Custom.type && !!query.value.categoricalChartStartTime && !!query.value.categoricalChartEndTime;
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
return query.value.trendChartDateType === DateRange.Custom.type && !!query.value.trendChartStartYearMonth && !!query.value.trendChartEndYearMonth;
} else {
return false;
}
});
const showAmountInChart = computed<boolean>(() => {
if (!showAccountBalance.value
&& (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type)) {
return false;
}
return true;
});
const totalAmountName = computed<string>(() => {
if (query.value.chartDataType === ChartDataType.IncomeByAccount.type
|| query.value.chartDataType === ChartDataType.IncomeByPrimaryCategory.type
|| query.value.chartDataType === ChartDataType.IncomeBySecondaryCategory.type) {
return tt('Total Income');
} else if (query.value.chartDataType === ChartDataType.ExpenseByAccount.type
|| query.value.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type
|| query.value.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type) {
return tt('Total Expense');
} else if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type) {
return tt('Total Assets');
} else if (query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
return tt('Total Liabilities');
}
return tt('Total Amount');
});
const showTotalAmountInTrendsChart = computed<boolean>(() => {
return query.value.chartDataType !== ChartDataType.TotalExpense.type &&
query.value.chartDataType !== ChartDataType.TotalIncome.type &&
query.value.chartDataType !== ChartDataType.TotalBalance.type;
});
const translateNameInTrendsChart = computed<boolean>(() => {
return query.value.chartDataType === ChartDataType.TotalExpense.type ||
query.value.chartDataType === ChartDataType.TotalIncome.type ||
query.value.chartDataType === ChartDataType.TotalBalance.type;
});
const categoricalAnalysisData = computed<TransactionCategoricalAnalysisData>(() => statisticsStore.categoricalAnalysisData);
const trendsAnalysisData = computed<TransactionTrendsAnalysisData | null>(() => statisticsStore.trendsAnalysisData);
function getDisplayAmount(amount: number, currency: string, textLimit?: number): string {
const finalAmount = formatAmountWithCurrency(amount, currency) as string;
if (!showAccountBalance.value
&& (query.value.chartDataType === ChartDataType.AccountTotalAssets.type
|| query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type)
) {
return '***';
}
if (textLimit) {
return limitText(finalAmount, textLimit);
}
return finalAmount;
}
return {
// states
loading,
analysisType,
trendDateAggregationType,
// computed states
showAccountBalance,
defaultCurrency,
firstDayOfWeek,
allDateRanges,
allSortingTypes,
allDateAggregationTypes,
query,
queryChartDataCategory,
queryDateType,
queryStartTime,
queryEndTime,
queryDateRangeName,
queryChartDataTypeName,
querySortingTypeName,
queryTrendDateAggregationTypeName,
isQueryDateRangeChanged,
canShiftDateRange,
showCustomDateRange,
showAmountInChart,
totalAmountName,
showTotalAmountInTrendsChart,
translateNameInTrendsChart,
categoricalAnalysisData,
trendsAnalysisData,
// functions
getDisplayAmount
};
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff