mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-19 09:14:27 +08:00
add transaction calendar
This commit is contained in:
@@ -317,6 +317,15 @@ export function getThisMonthLastUnixTime(): number {
|
|||||||
return moment.unix(getThisMonthFirstUnixTime()).add(1, 'months').subtract(1, 'seconds').unix();
|
return moment.unix(getThisMonthFirstUnixTime()).add(1, 'months').subtract(1, 'seconds').unix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMonthFirstUnixTimeBySpecifiedUnixTime(unixTime: number): number {
|
||||||
|
const date = moment.unix(unixTime).set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
|
||||||
|
return date.subtract(date.date() - 1, 'days').unix();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMonthLastUnixTimeBySpecifiedUnixTime(unixTime: number): number {
|
||||||
|
return moment.unix(getMonthFirstUnixTimeBySpecifiedUnixTime(unixTime)).add(1, 'months').subtract(1, 'seconds').unix();
|
||||||
|
}
|
||||||
|
|
||||||
export function getThisMonthSpecifiedDayFirstUnixTime(date: number): number {
|
export function getThisMonthSpecifiedDayFirstUnixTime(date: number): number {
|
||||||
return moment().set({ date: date, hour: 0, minute: 0, second: 0, millisecond: 0 }).unix();
|
return moment().set({ date: date, hour: 0, minute: 0, second: 0, millisecond: 0 }).unix();
|
||||||
}
|
}
|
||||||
@@ -792,6 +801,28 @@ export function getRecentDateRangeType(allRecentMonthDateRanges: LocalizedRecent
|
|||||||
return getRecentDateRangeTypeByDateType(allRecentMonthDateRanges, DateRange.Custom.type);
|
return getRecentDateRangeTypeByDateType(allRecentMonthDateRanges, DateRange.Custom.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getFullMonthDateRange(minTime: number, maxTime: number, firstDayOfWeek: number): TimeRangeAndDateType | null {
|
||||||
|
if (isDateRangeMatchOneMonth(minTime, maxTime)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!minTime) {
|
||||||
|
return getDateRangeByDateType(DateRange.ThisMonth.type, firstDayOfWeek);
|
||||||
|
}
|
||||||
|
|
||||||
|
const monthFirstUnixTime = getMonthFirstUnixTimeBySpecifiedUnixTime(minTime);
|
||||||
|
const monthLastUnixTime = getMonthLastUnixTimeBySpecifiedUnixTime(minTime);
|
||||||
|
const dateType = getDateTypeByDateRange(monthFirstUnixTime, monthLastUnixTime, firstDayOfWeek, DateRangeScene.Normal);
|
||||||
|
|
||||||
|
const dateRange: TimeRangeAndDateType = {
|
||||||
|
dateType: dateType,
|
||||||
|
maxTime: monthLastUnixTime,
|
||||||
|
minTime: monthFirstUnixTime
|
||||||
|
};
|
||||||
|
|
||||||
|
return dateRange;
|
||||||
|
}
|
||||||
|
|
||||||
export function getTimeValues(date: Date, is24Hour: boolean, isMeridiemIndicatorFirst: boolean): string[] {
|
export function getTimeValues(date: Date, is24Hour: boolean, isMeridiemIndicatorFirst: boolean): string[] {
|
||||||
const hourMinuteSeconds = [
|
const hourMinuteSeconds = [
|
||||||
getTwoDigitsString(is24Hour ? date.getHours() : getHourIn12HourFormat(date.getHours())),
|
getTwoDigitsString(is24Hour ? date.getHours() : getHourIn12HourFormat(date.getHours())),
|
||||||
@@ -849,6 +880,20 @@ export function getCombinedDateAndTimeValues(date: Date, timeValues: string[], i
|
|||||||
return newDateTime;
|
return newDateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMonthFirstDayOrCurrentDayShortDate(unixTime: number): string {
|
||||||
|
const currentTime = moment();
|
||||||
|
let dateTime = moment.unix(unixTime);
|
||||||
|
|
||||||
|
if (dateTime.year() === currentTime.year() && dateTime.month() === currentTime.month()) {
|
||||||
|
return getShortDate(currentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
dateTime = dateTime.set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
|
||||||
|
dateTime = dateTime.subtract(dateTime.date() - 1, 'days');
|
||||||
|
|
||||||
|
return getShortDate(dateTime);
|
||||||
|
}
|
||||||
|
|
||||||
export function isDateRangeMatchFullYears(minTime: number, maxTime: number): boolean {
|
export function isDateRangeMatchFullYears(minTime: number, maxTime: number): boolean {
|
||||||
const minDateTime = parseDateFromUnixTime(minTime).set({ millisecond: 0 });
|
const minDateTime = parseDateFromUnixTime(minTime).set({ millisecond: 0 });
|
||||||
const maxDateTime = parseDateFromUnixTime(maxTime).set({ millisecond: 999 });
|
const maxDateTime = parseDateFromUnixTime(maxTime).set({ millisecond: 999 });
|
||||||
|
|||||||
@@ -1508,6 +1508,7 @@
|
|||||||
"Income and Expense Trends": "Einkommens- und Ausgabentrends",
|
"Income and Expense Trends": "Einkommens- und Ausgabentrends",
|
||||||
"View Details": "Details anzeigen",
|
"View Details": "Details anzeigen",
|
||||||
"Transaction List": "Transaktionsliste",
|
"Transaction List": "Transaktionsliste",
|
||||||
|
"Transaction Calendar": "Transaction Calendar",
|
||||||
"Transaction Details": "Transaktionsdetails",
|
"Transaction Details": "Transaktionsdetails",
|
||||||
"Statistics & Analysis": "Statistiken & Analysen",
|
"Statistics & Analysis": "Statistiken & Analysen",
|
||||||
"Account List": "Kontoliste",
|
"Account List": "Kontoliste",
|
||||||
|
|||||||
@@ -1508,6 +1508,7 @@
|
|||||||
"Income and Expense Trends": "Income and Expense Trends",
|
"Income and Expense Trends": "Income and Expense Trends",
|
||||||
"View Details": "View Details",
|
"View Details": "View Details",
|
||||||
"Transaction List": "Transaction List",
|
"Transaction List": "Transaction List",
|
||||||
|
"Transaction Calendar": "Transaction Calendar",
|
||||||
"Transaction Details": "Transaction Details",
|
"Transaction Details": "Transaction Details",
|
||||||
"Statistics & Analysis": "Statistics & Analysis",
|
"Statistics & Analysis": "Statistics & Analysis",
|
||||||
"Account List": "Account List",
|
"Account List": "Account List",
|
||||||
|
|||||||
@@ -1507,6 +1507,7 @@
|
|||||||
"Income and Expense Trends": "Tendencias de ingresos y gastos",
|
"Income and Expense Trends": "Tendencias de ingresos y gastos",
|
||||||
"View Details": "Ver detalles",
|
"View Details": "Ver detalles",
|
||||||
"Transaction List": "Lista de transacciones",
|
"Transaction List": "Lista de transacciones",
|
||||||
|
"Transaction Calendar": "Transaction Calendar",
|
||||||
"Transaction Details": "Detalles de la transacción",
|
"Transaction Details": "Detalles de la transacción",
|
||||||
"Statistics & Analysis": "Estadísticas y análisis",
|
"Statistics & Analysis": "Estadísticas y análisis",
|
||||||
"Account List": "Lista de cuentas",
|
"Account List": "Lista de cuentas",
|
||||||
|
|||||||
@@ -1508,6 +1508,7 @@
|
|||||||
"Income and Expense Trends": "Andamento entrate e uscite",
|
"Income and Expense Trends": "Andamento entrate e uscite",
|
||||||
"View Details": "Visualizza dettagli",
|
"View Details": "Visualizza dettagli",
|
||||||
"Transaction List": "Elenco transazioni",
|
"Transaction List": "Elenco transazioni",
|
||||||
|
"Transaction Calendar": "Transaction Calendar",
|
||||||
"Transaction Details": "Dettagli transazione",
|
"Transaction Details": "Dettagli transazione",
|
||||||
"Statistics & Analysis": "Statistiche e analisi",
|
"Statistics & Analysis": "Statistiche e analisi",
|
||||||
"Account List": "Elenco account",
|
"Account List": "Elenco account",
|
||||||
|
|||||||
@@ -1508,6 +1508,7 @@
|
|||||||
"Income and Expense Trends": "収入と支出の傾向",
|
"Income and Expense Trends": "収入と支出の傾向",
|
||||||
"View Details": "詳細を表示",
|
"View Details": "詳細を表示",
|
||||||
"Transaction List": "取引リスト",
|
"Transaction List": "取引リスト",
|
||||||
|
"Transaction Calendar": "Transaction Calendar",
|
||||||
"Transaction Details": "取引の詳細",
|
"Transaction Details": "取引の詳細",
|
||||||
"Statistics & Analysis": "統計と分析",
|
"Statistics & Analysis": "統計と分析",
|
||||||
"Account List": "口座リスト",
|
"Account List": "口座リスト",
|
||||||
|
|||||||
@@ -1508,6 +1508,7 @@
|
|||||||
"Income and Expense Trends": "Тенденции доходов и расходов",
|
"Income and Expense Trends": "Тенденции доходов и расходов",
|
||||||
"View Details": "Просмотреть детали",
|
"View Details": "Просмотреть детали",
|
||||||
"Transaction List": "Список транзакций",
|
"Transaction List": "Список транзакций",
|
||||||
|
"Transaction Calendar": "Transaction Calendar",
|
||||||
"Transaction Details": "Детали транзакции",
|
"Transaction Details": "Детали транзакции",
|
||||||
"Statistics & Analysis": "Статистика и анализ",
|
"Statistics & Analysis": "Статистика и анализ",
|
||||||
"Account List": "Список счетов",
|
"Account List": "Список счетов",
|
||||||
|
|||||||
@@ -1508,6 +1508,7 @@
|
|||||||
"Income and Expense Trends": "Тренди доходів і витрат",
|
"Income and Expense Trends": "Тренди доходів і витрат",
|
||||||
"View Details": "Переглянути деталі",
|
"View Details": "Переглянути деталі",
|
||||||
"Transaction List": "Список транзакцій",
|
"Transaction List": "Список транзакцій",
|
||||||
|
"Transaction Calendar": "Transaction Calendar",
|
||||||
"Transaction Details": "Деталі по транзакціях",
|
"Transaction Details": "Деталі по транзакціях",
|
||||||
"Statistics & Analysis": "Статистика та аналіз",
|
"Statistics & Analysis": "Статистика та аналіз",
|
||||||
"Account List": "Список рахунків",
|
"Account List": "Список рахунків",
|
||||||
|
|||||||
@@ -1508,6 +1508,7 @@
|
|||||||
"Income and Expense Trends": "Xu hướng thu nhập và chi tiêu",
|
"Income and Expense Trends": "Xu hướng thu nhập và chi tiêu",
|
||||||
"View Details": "Xem chi tiết",
|
"View Details": "Xem chi tiết",
|
||||||
"Transaction List": "Danh sách giao dịch",
|
"Transaction List": "Danh sách giao dịch",
|
||||||
|
"Transaction Calendar": "Transaction Calendar",
|
||||||
"Transaction Details": "Chi tiết giao dịch",
|
"Transaction Details": "Chi tiết giao dịch",
|
||||||
"Statistics & Analysis": "Thống kê & Phân tích",
|
"Statistics & Analysis": "Thống kê & Phân tích",
|
||||||
"Account List": "Danh sách tài khoản",
|
"Account List": "Danh sách tài khoản",
|
||||||
|
|||||||
@@ -1508,6 +1508,7 @@
|
|||||||
"Income and Expense Trends": "收入与支出趋势",
|
"Income and Expense Trends": "收入与支出趋势",
|
||||||
"View Details": "查看详情",
|
"View Details": "查看详情",
|
||||||
"Transaction List": "交易列表",
|
"Transaction List": "交易列表",
|
||||||
|
"Transaction Calendar": "交易日历",
|
||||||
"Transaction Details": "交易详情",
|
"Transaction Details": "交易详情",
|
||||||
"Statistics & Analysis": "统计分析",
|
"Statistics & Analysis": "统计分析",
|
||||||
"Account List": "账户列表",
|
"Account List": "账户列表",
|
||||||
|
|||||||
@@ -1508,6 +1508,7 @@
|
|||||||
"Income and Expense Trends": "收入與支出趨勢",
|
"Income and Expense Trends": "收入與支出趨勢",
|
||||||
"View Details": "查看詳情",
|
"View Details": "查看詳情",
|
||||||
"Transaction List": "交易清單",
|
"Transaction List": "交易清單",
|
||||||
|
"Transaction Calendar": "交易日曆",
|
||||||
"Transaction Details": "交易詳情",
|
"Transaction Details": "交易詳情",
|
||||||
"Statistics & Analysis": "統計分析",
|
"Statistics & Analysis": "統計分析",
|
||||||
"Account List": "帳戶清單",
|
"Account List": "帳戶清單",
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ const router = createRouter({
|
|||||||
component: TransactionListPage,
|
component: TransactionListPage,
|
||||||
beforeEnter: checkLogin,
|
beforeEnter: checkLogin,
|
||||||
props: route => ({
|
props: route => ({
|
||||||
|
initPageType: route.query['pageType'],
|
||||||
initDateType: route.query['dateType'],
|
initDateType: route.query['dateType'],
|
||||||
initMaxTime: route.query['maxTime'],
|
initMaxTime: route.query['maxTime'],
|
||||||
initMinTime: route.query['minTime'],
|
initMinTime: route.query['minTime'],
|
||||||
|
|||||||
@@ -87,18 +87,21 @@ export interface TransactionListFilter extends TransactionListPartialFilter {
|
|||||||
keyword: string;
|
keyword: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TransactionTotalAmount {
|
||||||
|
expense: number;
|
||||||
|
incompleteExpense: boolean;
|
||||||
|
income: number;
|
||||||
|
incompleteIncome: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface TransactionMonthList {
|
export interface TransactionMonthList {
|
||||||
readonly year: number;
|
readonly year: number;
|
||||||
readonly month: number;
|
readonly month: number;
|
||||||
readonly yearMonth: string;
|
readonly yearMonth: string;
|
||||||
opened: boolean;
|
opened: boolean;
|
||||||
readonly items: Transaction[];
|
readonly items: Transaction[];
|
||||||
readonly totalAmount: {
|
readonly totalAmount: TransactionTotalAmount;
|
||||||
expense: number;
|
readonly dailyTotalAmounts: Record<string, TransactionTotalAmount>;
|
||||||
incompleteExpense: boolean;
|
|
||||||
income: number;
|
|
||||||
incompleteIncome: boolean;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useTransactionsStore = defineStore('transactions', () => {
|
export const useTransactionsStore = defineStore('transactions', () => {
|
||||||
@@ -216,7 +219,8 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
incompleteExpense: true,
|
incompleteExpense: true,
|
||||||
income: 0,
|
income: 0,
|
||||||
incompleteIncome: true
|
incompleteIncome: true
|
||||||
}
|
},
|
||||||
|
dailyTotalAmounts: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
transactions.value.push(monthList);
|
transactions.value.push(monthList);
|
||||||
@@ -319,6 +323,7 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
let totalIncome = 0;
|
let totalIncome = 0;
|
||||||
let hasUnCalculatedTotalExpense = false;
|
let hasUnCalculatedTotalExpense = false;
|
||||||
let hasUnCalculatedTotalIncome = false;
|
let hasUnCalculatedTotalIncome = false;
|
||||||
|
const dailyTotalAmounts: Record<string, TransactionTotalAmount> = {};
|
||||||
|
|
||||||
const allAccountIdsMap: Record<string, boolean> = {};
|
const allAccountIdsMap: Record<string, boolean> = {};
|
||||||
let totalAccountIdsCount = 0;
|
let totalAccountIdsCount = 0;
|
||||||
@@ -336,6 +341,18 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
|
|
||||||
for (let i = 0; i < transactionMonthList.items.length; i++) {
|
for (let i = 0; i < transactionMonthList.items.length; i++) {
|
||||||
const transaction = transactionMonthList.items[i];
|
const transaction = transactionMonthList.items[i];
|
||||||
|
const transactionDay = isNumber(transaction.day) ? transaction.day.toString() : '0';
|
||||||
|
let dailyTotalAmount = dailyTotalAmounts[transactionDay];
|
||||||
|
|
||||||
|
if (!dailyTotalAmount) {
|
||||||
|
dailyTotalAmount = {
|
||||||
|
expense: 0,
|
||||||
|
incompleteExpense: false,
|
||||||
|
income: 0,
|
||||||
|
incompleteIncome: false
|
||||||
|
};
|
||||||
|
dailyTotalAmounts[transactionDay] = dailyTotalAmount;
|
||||||
|
}
|
||||||
|
|
||||||
let amount = transaction.sourceAmount;
|
let amount = transaction.sourceAmount;
|
||||||
let account = transaction.sourceAccount;
|
let account = transaction.sourceAccount;
|
||||||
@@ -357,8 +374,10 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
if (!isNumber(balance)) {
|
if (!isNumber(balance)) {
|
||||||
if (transaction.type === TransactionType.Expense) {
|
if (transaction.type === TransactionType.Expense) {
|
||||||
hasUnCalculatedTotalExpense = true;
|
hasUnCalculatedTotalExpense = true;
|
||||||
|
dailyTotalAmount.incompleteExpense = true;
|
||||||
} else if (transaction.type === TransactionType.Income) {
|
} else if (transaction.type === TransactionType.Income) {
|
||||||
hasUnCalculatedTotalIncome = true;
|
hasUnCalculatedTotalIncome = true;
|
||||||
|
dailyTotalAmount.incompleteIncome = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -369,8 +388,10 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
|
|
||||||
if (transaction.type === TransactionType.Expense) {
|
if (transaction.type === TransactionType.Expense) {
|
||||||
totalExpense += amount;
|
totalExpense += amount;
|
||||||
|
dailyTotalAmount.expense += amount;
|
||||||
} else if (transaction.type === TransactionType.Income) {
|
} else if (transaction.type === TransactionType.Income) {
|
||||||
totalIncome += amount;
|
totalIncome += amount;
|
||||||
|
dailyTotalAmount.income += amount;
|
||||||
} else if (transaction.type === TransactionType.Transfer && totalAccountIdsCount > 0) {
|
} else if (transaction.type === TransactionType.Transfer && totalAccountIdsCount > 0) {
|
||||||
if (allAccountIdsMap[transaction.sourceAccountId] && allAccountIdsMap[transaction.destinationAccountId]) {
|
if (allAccountIdsMap[transaction.sourceAccountId] && allAccountIdsMap[transaction.destinationAccountId]) {
|
||||||
// Do Nothing
|
// Do Nothing
|
||||||
@@ -382,8 +403,10 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
// Do Nothing
|
// Do Nothing
|
||||||
} else if (allAccountIdsMap[transaction.sourceAccountId] || (transaction.sourceAccount && allAccountIdsMap[transaction.sourceAccount.parentId])) {
|
} else if (allAccountIdsMap[transaction.sourceAccountId] || (transaction.sourceAccount && allAccountIdsMap[transaction.sourceAccount.parentId])) {
|
||||||
totalExpense += amount;
|
totalExpense += amount;
|
||||||
|
dailyTotalAmount.expense += amount;
|
||||||
} else if (allAccountIdsMap[transaction.destinationAccountId] || (transaction.destinationAccount && allAccountIdsMap[transaction.destinationAccount.parentId])) {
|
} else if (allAccountIdsMap[transaction.destinationAccountId] || (transaction.destinationAccount && allAccountIdsMap[transaction.destinationAccount.parentId])) {
|
||||||
totalIncome += amount;
|
totalIncome += amount;
|
||||||
|
dailyTotalAmount.income += amount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,6 +415,29 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
transactionMonthList.totalAmount.incompleteExpense = incomplete || hasUnCalculatedTotalExpense;
|
transactionMonthList.totalAmount.incompleteExpense = incomplete || hasUnCalculatedTotalExpense;
|
||||||
transactionMonthList.totalAmount.income = Math.floor(totalIncome);
|
transactionMonthList.totalAmount.income = Math.floor(totalIncome);
|
||||||
transactionMonthList.totalAmount.incompleteIncome = incomplete || hasUnCalculatedTotalIncome;
|
transactionMonthList.totalAmount.incompleteIncome = incomplete || hasUnCalculatedTotalIncome;
|
||||||
|
|
||||||
|
for (const day in transactionMonthList.dailyTotalAmounts) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(transactionMonthList.dailyTotalAmounts, day)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete transactionMonthList.dailyTotalAmounts[day];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const day in dailyTotalAmounts) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(dailyTotalAmounts, day)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dailyTotalAmount = dailyTotalAmounts[day];
|
||||||
|
|
||||||
|
transactionMonthList.dailyTotalAmounts[day] = {
|
||||||
|
expense: Math.floor(dailyTotalAmount.expense),
|
||||||
|
incompleteExpense: incomplete || dailyTotalAmount.incompleteExpense,
|
||||||
|
income: Math.floor(dailyTotalAmount.income),
|
||||||
|
incompleteIncome: incomplete || dailyTotalAmount.incompleteIncome
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillTransactionObject(transaction: Transaction, currentUtcOffset: number): void {
|
function fillTransactionObject(transaction: Transaction, currentUtcOffset: number): void {
|
||||||
@@ -691,9 +737,11 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTransactionListPageParams(): string {
|
function getTransactionListPageParams(pageType: number): string {
|
||||||
const querys: string[] = [];
|
const querys: string[] = [];
|
||||||
|
|
||||||
|
querys.push('pageType=' + pageType);
|
||||||
|
|
||||||
if (transactionsFilter.value.type) {
|
if (transactionsFilter.value.type) {
|
||||||
querys.push('type=' + transactionsFilter.value.type);
|
querys.push('type=' + transactionsFilter.value.type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
|||||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||||
import { type TransactionListFilter, type TransactionMonthList, useTransactionsStore } from '@/stores/transaction.ts';
|
import { type TransactionListFilter, type TransactionMonthList, useTransactionsStore } from '@/stores/transaction.ts';
|
||||||
|
|
||||||
|
import type { TypeAndName } from '@/core/base.ts';
|
||||||
import { type LocalizedDateRange, DateRange, DateRangeScene } from '@/core/datetime.ts';
|
import { type LocalizedDateRange, DateRange, DateRangeScene } from '@/core/datetime.ts';
|
||||||
import { AccountType } from '@/core/account.ts';
|
import { AccountType } from '@/core/account.ts';
|
||||||
import { TransactionType } from '@/core/transaction.ts';
|
import { TransactionType } from '@/core/transaction.ts';
|
||||||
@@ -18,6 +19,10 @@ import type { TransactionCategory } from '@/models/transaction_category.ts';
|
|||||||
import type { TransactionTag } from '@/models/transaction_tag.ts';
|
import type { TransactionTag } from '@/models/transaction_tag.ts';
|
||||||
import type { Transaction } from '@/models/transaction.ts';
|
import type { Transaction } from '@/models/transaction.ts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
arrangeArrayWithNewStartIndex
|
||||||
|
} from '@/lib/common.ts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getUtcOffsetByUtcOffsetMinutes,
|
getUtcOffsetByUtcOffsetMinutes,
|
||||||
getTimezoneOffset,
|
getTimezoneOffset,
|
||||||
@@ -35,9 +40,39 @@ import {
|
|||||||
categoryTypeToTransactionType
|
categoryTypeToTransactionType
|
||||||
} from '@/lib/category.ts';
|
} from '@/lib/category.ts';
|
||||||
|
|
||||||
|
export class TransactionListPageType implements TypeAndName {
|
||||||
|
private static readonly allInstances: TransactionListPageType[] = [];
|
||||||
|
private static readonly allInstancesByType: Record<number, TransactionListPageType> = {};
|
||||||
|
|
||||||
|
public static readonly List = new TransactionListPageType(0, 'Transaction List');
|
||||||
|
public static readonly Calendar = new TransactionListPageType(1, 'Transaction Calendar');
|
||||||
|
|
||||||
|
public static readonly Default = TransactionListPageType.List;
|
||||||
|
|
||||||
|
public readonly type: number;
|
||||||
|
public readonly name: string;
|
||||||
|
|
||||||
|
private constructor(type: number, name: string) {
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
|
||||||
|
TransactionListPageType.allInstances.push(this);
|
||||||
|
TransactionListPageType.allInstancesByType[type] = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static values(): TransactionListPageType[] {
|
||||||
|
return TransactionListPageType.allInstances;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static valueOf(type: number): TransactionListPageType | undefined {
|
||||||
|
return TransactionListPageType.allInstancesByType[type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function useTransactionListPageBase() {
|
export function useTransactionListPageBase() {
|
||||||
const {
|
const {
|
||||||
tt,
|
tt,
|
||||||
|
getAllLongWeekdayNames,
|
||||||
getAllDateRanges,
|
getAllDateRanges,
|
||||||
formatUnixTimeToLongDateTime,
|
formatUnixTimeToLongDateTime,
|
||||||
formatUnixTimeToLongDate,
|
formatUnixTimeToLongDate,
|
||||||
@@ -54,12 +89,15 @@ export function useTransactionListPageBase() {
|
|||||||
const transactionTagsStore = useTransactionTagsStore();
|
const transactionTagsStore = useTransactionTagsStore();
|
||||||
const transactionsStore = useTransactionsStore();
|
const transactionsStore = useTransactionsStore();
|
||||||
|
|
||||||
|
const pageType = ref<number>(TransactionListPageType.List.type);
|
||||||
const loading = ref<boolean>(true);
|
const loading = ref<boolean>(true);
|
||||||
const customMinDatetime = ref<number>(0);
|
const customMinDatetime = ref<number>(0);
|
||||||
const customMaxDatetime = ref<number>(0);
|
const customMaxDatetime = ref<number>(0);
|
||||||
|
const currentCalendarDate = ref<string>('');
|
||||||
|
|
||||||
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone));
|
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone));
|
||||||
const firstDayOfWeek = computed<number>(() => userStore.currentUserFirstDayOfWeek);
|
const firstDayOfWeek = computed<number>(() => userStore.currentUserFirstDayOfWeek);
|
||||||
|
const dayNames = computed<string[]>(() => arrangeArrayWithNewStartIndex(getAllLongWeekdayNames(), firstDayOfWeek.value));
|
||||||
const defaultCurrency = computed<string>(() => getUnifiedSelectedAccountsCurrencyOrDefaultCurrency(allAccountsMap.value, queryAllFilterAccountIds.value, userStore.currentUserDefaultCurrency));
|
const defaultCurrency = computed<string>(() => getUnifiedSelectedAccountsCurrencyOrDefaultCurrency(allAccountsMap.value, queryAllFilterAccountIds.value, userStore.currentUserDefaultCurrency));
|
||||||
const showTotalAmountInTransactionListPage = computed<boolean>(() => settingsStore.appSettings.showTotalAmountInTransactionListPage);
|
const showTotalAmountInTransactionListPage = computed<boolean>(() => settingsStore.appSettings.showTotalAmountInTransactionListPage);
|
||||||
const showTagInTransactionListPage = computed<boolean>(() => settingsStore.appSettings.showTagInTransactionListPage);
|
const showTagInTransactionListPage = computed<boolean>(() => settingsStore.appSettings.showTagInTransactionListPage);
|
||||||
@@ -110,6 +148,16 @@ export function useTransactionListPageBase() {
|
|||||||
const allTransactionTags = computed<Record<string, TransactionTag>>(() => transactionTagsStore.allTransactionTagsMap);
|
const allTransactionTags = computed<Record<string, TransactionTag>>(() => transactionTagsStore.allTransactionTagsMap);
|
||||||
const allAvailableTagsCount = computed<number>(() => transactionTagsStore.allAvailableTagsCount);
|
const allAvailableTagsCount = computed<number>(() => transactionTagsStore.allAvailableTagsCount);
|
||||||
|
|
||||||
|
const displayPageTypeName = computed<string>(() => {
|
||||||
|
const type = TransactionListPageType.valueOf(pageType.value);
|
||||||
|
|
||||||
|
if (type) {
|
||||||
|
return tt(type.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tt(TransactionListPageType.List.name);
|
||||||
|
});
|
||||||
|
|
||||||
const query = computed<TransactionListFilter>(() => transactionsStore.transactionsFilter);
|
const query = computed<TransactionListFilter>(() => transactionsStore.transactionsFilter);
|
||||||
const queryDateRangeName = computed<string>(() => {
|
const queryDateRangeName = computed<string>(() => {
|
||||||
if (query.value.dateType === DateRange.All.type) {
|
if (query.value.dateType === DateRange.All.type) {
|
||||||
@@ -268,12 +316,15 @@ export function useTransactionListPageBase() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
// states
|
// states
|
||||||
|
pageType,
|
||||||
loading,
|
loading,
|
||||||
customMinDatetime,
|
customMinDatetime,
|
||||||
customMaxDatetime,
|
customMaxDatetime,
|
||||||
|
currentCalendarDate,
|
||||||
// computed states
|
// computed states
|
||||||
currentTimezoneOffsetMinutes,
|
currentTimezoneOffsetMinutes,
|
||||||
firstDayOfWeek,
|
firstDayOfWeek,
|
||||||
|
dayNames,
|
||||||
defaultCurrency,
|
defaultCurrency,
|
||||||
showTotalAmountInTransactionListPage,
|
showTotalAmountInTransactionListPage,
|
||||||
showTagInTransactionListPage,
|
showTagInTransactionListPage,
|
||||||
@@ -286,6 +337,7 @@ export function useTransactionListPageBase() {
|
|||||||
allAvailableCategoriesCount,
|
allAvailableCategoriesCount,
|
||||||
allTransactionTags,
|
allTransactionTags,
|
||||||
allAvailableTagsCount,
|
allAvailableTagsCount,
|
||||||
|
displayPageTypeName,
|
||||||
query,
|
query,
|
||||||
queryDateRangeName,
|
queryDateRangeName,
|
||||||
queryMinTime,
|
queryMinTime,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-link">
|
<li class="nav-link">
|
||||||
<router-link to="/transaction/list?dateType=7">
|
<router-link to="/transaction/list?pageType=0&dateType=7">
|
||||||
<v-icon class="nav-item-icon" :icon="mdiListBoxOutline"/>
|
<v-icon class="nav-item-icon" :icon="mdiListBoxOutline"/>
|
||||||
<span class="nav-item-title d-inline-block">{{ tt('Transaction Details') }}</span>
|
<span class="nav-item-title d-inline-block">{{ tt('Transaction Details') }}</span>
|
||||||
<v-btn density="compact" color="secondary" variant="text" size="22"
|
<v-btn density="compact" color="secondary" variant="text" size="22"
|
||||||
|
|||||||
@@ -5,16 +5,33 @@
|
|||||||
<v-layout>
|
<v-layout>
|
||||||
<v-navigation-drawer :permanent="alwaysShowNav" v-model="showNav">
|
<v-navigation-drawer :permanent="alwaysShowNav" v-model="showNav">
|
||||||
<div class="mx-6 my-4">
|
<div class="mx-6 my-4">
|
||||||
<btn-vertical-group :disabled="loading" :buttons="[
|
<btn-vertical-group :disabled="loading" :buttons="TransactionListPageType.values().map(item => {
|
||||||
{ name: tt('All Types'), value: 0 },
|
return {
|
||||||
{ name: tt('Modify Balance'), value: 1 },
|
name: tt(item.name),
|
||||||
{ name: tt('Income'), value: 2 },
|
value: item.type
|
||||||
{ name: tt('Expense'), value: 3 },
|
}
|
||||||
{ name: tt('Transfer'), value: 4 }
|
})" v-model="queryPageType" />
|
||||||
]" v-model="queryType" />
|
|
||||||
</div>
|
</div>
|
||||||
<v-divider />
|
<v-divider />
|
||||||
<div class="mx-6 mt-4">
|
<div class="mx-6 mt-4">
|
||||||
|
<span class="text-subtitle-2">{{ tt('Transaction Type') }}</span>
|
||||||
|
<v-select
|
||||||
|
item-title="displayName"
|
||||||
|
item-value="type"
|
||||||
|
class="mt-2"
|
||||||
|
density="compact"
|
||||||
|
:disabled="loading"
|
||||||
|
:items="[
|
||||||
|
{ displayName: tt('All Types'), type: 0 },
|
||||||
|
{ displayName: tt('Modify Balance'), type: 1 },
|
||||||
|
{ displayName: tt('Income'), type: 2 },
|
||||||
|
{ displayName: tt('Expense'), type: 3 },
|
||||||
|
{ displayName: tt('Transfer'), type: 4 }
|
||||||
|
]"
|
||||||
|
v-model="queryType"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mx-6 mt-4" v-if="pageType === TransactionListPageType.List.type">
|
||||||
<span class="text-subtitle-2">{{ tt('Transactions Per Page') }}</span>
|
<span class="text-subtitle-2">{{ tt('Transactions Per Page') }}</span>
|
||||||
<v-select class="mt-2" density="compact" :disabled="loading"
|
<v-select class="mt-2" density="compact" :disabled="loading"
|
||||||
:items="[ 5, 10, 15, 20, 25, 30, 50 ]"
|
:items="[ 5, 10, 15, 20, 25, 30, 50 ]"
|
||||||
@@ -119,6 +136,39 @@
|
|||||||
</div>
|
</div>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-text class="transaction-calendar-container pt-0" v-if="pageType === TransactionListPageType.Calendar.type">
|
||||||
|
<vue-date-picker inline auto-apply model-type="yyyy-M-d"
|
||||||
|
month-name-format="long"
|
||||||
|
class="justify-content-center"
|
||||||
|
:disable-month-year-select="true"
|
||||||
|
:month-change-on-scroll="false"
|
||||||
|
:month-change-on-arrows="false"
|
||||||
|
:enable-time-picker="false"
|
||||||
|
:hide-offset-dates="true"
|
||||||
|
:min-date="getShortDate(parseDateFromUnixTime(query.minTime))"
|
||||||
|
:max-date="getShortDate(parseDateFromUnixTime(query.maxTime))"
|
||||||
|
:disabled-dates="noTransactionInMonthDay"
|
||||||
|
:clearable="false"
|
||||||
|
:dark="isDarkMode"
|
||||||
|
:week-start="firstDayOfWeek"
|
||||||
|
:day-names="dayNames"
|
||||||
|
v-model="currentCalendarDate">
|
||||||
|
<template #month="{ text }">
|
||||||
|
{{ getMonthShortName(text) }}
|
||||||
|
</template>
|
||||||
|
<template #month-overlay-value="{ text }">
|
||||||
|
{{ getMonthShortName(text) }}
|
||||||
|
</template>
|
||||||
|
<template #day="{ day }">
|
||||||
|
<div class="block">
|
||||||
|
<div :class="{ 'font-weight-bold': currentMonthTransactionData && currentMonthTransactionData.dailyTotalAmounts[day] }">{{ day }}</div>
|
||||||
|
<div class="text-income" v-if="currentMonthTransactionData && currentMonthTransactionData.dailyTotalAmounts[day] && currentMonthTransactionData.dailyTotalAmounts[day].income">{{ getDisplayMonthTotalAmount(currentMonthTransactionData.dailyTotalAmounts[day].income, defaultCurrency, '', currentMonthTransactionData.dailyTotalAmounts[day].incompleteIncome) }}</div>
|
||||||
|
<div class="text-expense" v-if="currentMonthTransactionData && currentMonthTransactionData.dailyTotalAmounts[day] && currentMonthTransactionData.dailyTotalAmounts[day].expense">{{ getDisplayMonthTotalAmount(currentMonthTransactionData.dailyTotalAmounts[day].expense, defaultCurrency, '', currentMonthTransactionData.dailyTotalAmounts[day].incompleteExpense) }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</vue-date-picker>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
<v-table class="transaction-table" :hover="!loading">
|
<v-table class="transaction-table" :hover="!loading">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -471,7 +521,7 @@
|
|||||||
:class="{ 'disabled': loading, 'has-bottom-border': idx < transactions.length - 1 }"
|
:class="{ 'disabled': loading, 'has-bottom-border': idx < transactions.length - 1 }"
|
||||||
v-for="(transaction, idx) in transactions">
|
v-for="(transaction, idx) in transactions">
|
||||||
<tr class="transaction-list-row-date no-hover text-sm"
|
<tr class="transaction-list-row-date no-hover text-sm"
|
||||||
v-if="idx === 0 || (idx > 0 && (transaction.date !== transactions[idx - 1].date))">
|
v-if="pageType === TransactionListPageType.List.type && (idx === 0 || (idx > 0 && (transaction.date !== transactions[idx - 1].date)))">
|
||||||
<td :colspan="showTagInTransactionListPage ? 6 : 5" class="font-weight-bold">
|
<td :colspan="showTagInTransactionListPage ? 6 : 5" class="font-weight-bold">
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">
|
||||||
<span>{{ getDisplayLongDate(transaction) }}</span>
|
<span>{{ getDisplayLongDate(transaction) }}</span>
|
||||||
@@ -538,7 +588,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</v-table>
|
</v-table>
|
||||||
|
|
||||||
<div class="mt-2 mb-4">
|
<div class="mt-2 mb-4" v-if="pageType === TransactionListPageType.List.type">
|
||||||
<pagination-buttons :totalPageCount="totalPageCount"
|
<pagination-buttons :totalPageCount="totalPageCount"
|
||||||
v-model="paginationCurrentPage"></pagination-buttons>
|
v-model="paginationCurrentPage"></pagination-buttons>
|
||||||
</div>
|
</div>
|
||||||
@@ -593,10 +643,10 @@ import { TransactionEditPageType } from '@/views/base/transactions/TransactionEd
|
|||||||
|
|
||||||
import { ref, computed, useTemplateRef, watch, nextTick } from 'vue';
|
import { ref, computed, useTemplateRef, watch, nextTick } from 'vue';
|
||||||
import { useRouter, onBeforeRouteUpdate } from 'vue-router';
|
import { useRouter, onBeforeRouteUpdate } from 'vue-router';
|
||||||
import { useDisplay } from 'vuetify';
|
import { useDisplay, useTheme } from 'vuetify';
|
||||||
|
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
import { useTransactionListPageBase } from '@/views/base/transactions/TransactionListPageBase.ts';
|
import { TransactionListPageType, useTransactionListPageBase } from '@/views/base/transactions/TransactionListPageBase.ts';
|
||||||
|
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import { useSettingsStore } from '@/stores/setting.ts';
|
||||||
import { useAccountsStore } from '@/stores/account.ts';
|
import { useAccountsStore } from '@/stores/account.ts';
|
||||||
@@ -614,6 +664,7 @@ import {
|
|||||||
DateRange
|
DateRange
|
||||||
} from '@/core/datetime.ts';
|
} from '@/core/datetime.ts';
|
||||||
import { AmountFilterType } from '@/core/numeral.ts';
|
import { AmountFilterType } from '@/core/numeral.ts';
|
||||||
|
import { ThemeType } from '@/core/theme.ts';
|
||||||
import { TransactionType, TransactionTagFilterType } from '@/core/transaction.ts';
|
import { TransactionType, TransactionTagFilterType } from '@/core/transaction.ts';
|
||||||
import { TemplateType } from '@/core/template.ts';
|
import { TemplateType } from '@/core/template.ts';
|
||||||
import type { TransactionCategory } from '@/models/transaction_category.ts';
|
import type { TransactionCategory } from '@/models/transaction_category.ts';
|
||||||
@@ -628,8 +679,10 @@ import {
|
|||||||
import {
|
import {
|
||||||
getCurrentUnixTime,
|
getCurrentUnixTime,
|
||||||
parseDateFromUnixTime,
|
parseDateFromUnixTime,
|
||||||
|
getShortDate,
|
||||||
getYear,
|
getYear,
|
||||||
getMonth,
|
getMonth,
|
||||||
|
getDay,
|
||||||
getSpecifiedDayFirstUnixTime,
|
getSpecifiedDayFirstUnixTime,
|
||||||
getBrowserTimezoneOffsetMinutes,
|
getBrowserTimezoneOffsetMinutes,
|
||||||
getActualUnixTimeForStore,
|
getActualUnixTimeForStore,
|
||||||
@@ -640,6 +693,8 @@ import {
|
|||||||
getDateRangeByDateType,
|
getDateRangeByDateType,
|
||||||
getDateRangeByBillingCycleDateType,
|
getDateRangeByBillingCycleDateType,
|
||||||
getRecentDateRangeType,
|
getRecentDateRangeType,
|
||||||
|
getFullMonthDateRange,
|
||||||
|
getMonthFirstDayOrCurrentDayShortDate,
|
||||||
isDateRangeMatchOneMonth
|
isDateRangeMatchOneMonth
|
||||||
} from '@/lib/datetime.ts';
|
} from '@/lib/datetime.ts';
|
||||||
import {
|
import {
|
||||||
@@ -671,6 +726,7 @@ import {
|
|||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
|
|
||||||
interface TransactionListProps {
|
interface TransactionListProps {
|
||||||
|
initPageType?: string;
|
||||||
initDateType?: string,
|
initDateType?: string,
|
||||||
initMaxTime?: string,
|
initMaxTime?: string,
|
||||||
initMinTime?: string,
|
initMinTime?: string,
|
||||||
@@ -703,20 +759,25 @@ interface TransactionListDisplayTotalAmount {
|
|||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const display = useDisplay();
|
const display = useDisplay();
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
tt,
|
tt,
|
||||||
getAllRecentMonthDateRanges,
|
getAllRecentMonthDateRanges,
|
||||||
getAllTransactionTagFilterTypes,
|
getAllTransactionTagFilterTypes,
|
||||||
|
getMonthShortName,
|
||||||
getWeekdayLongName
|
getWeekdayLongName
|
||||||
} = useI18n();
|
} = useI18n();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
pageType,
|
||||||
loading,
|
loading,
|
||||||
customMinDatetime,
|
customMinDatetime,
|
||||||
customMaxDatetime,
|
customMaxDatetime,
|
||||||
|
currentCalendarDate,
|
||||||
currentTimezoneOffsetMinutes,
|
currentTimezoneOffsetMinutes,
|
||||||
firstDayOfWeek,
|
firstDayOfWeek,
|
||||||
|
dayNames,
|
||||||
defaultCurrency,
|
defaultCurrency,
|
||||||
showTotalAmountInTransactionListPage,
|
showTotalAmountInTransactionListPage,
|
||||||
showTagInTransactionListPage,
|
showTagInTransactionListPage,
|
||||||
@@ -796,7 +857,9 @@ const showFilterAccountDialog = ref<boolean>(false);
|
|||||||
const showFilterCategoryDialog = ref<boolean>(false);
|
const showFilterCategoryDialog = ref<boolean>(false);
|
||||||
const showFilterTagDialog = ref<boolean>(false);
|
const showFilterTagDialog = ref<boolean>(false);
|
||||||
|
|
||||||
const recentMonthDateRanges = computed<LocalizedRecentMonthDateRange[]>(() => getAllRecentMonthDateRanges(true, true));
|
const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark);
|
||||||
|
|
||||||
|
const recentMonthDateRanges = computed<LocalizedRecentMonthDateRange[]>(() => getAllRecentMonthDateRanges(pageType.value === TransactionListPageType.List.type, true));
|
||||||
|
|
||||||
const allTransactionTemplates = computed<TransactionTemplate[]>(() => {
|
const allTransactionTemplates = computed<TransactionTemplate[]>(() => {
|
||||||
const allTemplates = transactionTemplatesStore.allVisibleTemplates;
|
const allTemplates = transactionTemplatesStore.allVisibleTemplates;
|
||||||
@@ -827,19 +890,45 @@ const allowCategoryTypes = computed<string>(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const transactions = computed<Transaction[]>(() => {
|
const transactions = computed<Transaction[]>(() => {
|
||||||
if (queryMonthlyData.value) {
|
if (pageType.value === TransactionListPageType.List.type) {
|
||||||
const transactionData = currentMonthTransactionData.value;
|
if (queryMonthlyData.value) {
|
||||||
|
const transactionData = currentMonthTransactionData.value;
|
||||||
|
|
||||||
if (!transactionData || !transactionData.items) {
|
if (!transactionData || !transactionData.items) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstIndex = (currentPage.value - 1) * countPerPage.value;
|
||||||
|
const lastIndex = currentPage.value * countPerPage.value;
|
||||||
|
|
||||||
|
return transactionData.items.slice(firstIndex, lastIndex);
|
||||||
|
} else {
|
||||||
|
return currentPageTransactions.value;
|
||||||
|
}
|
||||||
|
} else if (pageType.value === TransactionListPageType.Calendar.type) {
|
||||||
|
if (queryMonthlyData.value) {
|
||||||
|
const transactionData = currentMonthTransactionData.value;
|
||||||
|
|
||||||
|
if (!transactionData || !transactionData.items) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const transactions :Transaction[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < transactionData.items.length; i++) {
|
||||||
|
const transaction = transactionData.items[i];
|
||||||
|
|
||||||
|
if (transaction.date === currentCalendarDate.value) {
|
||||||
|
transactions.push(transaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactions;
|
||||||
|
} else {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstIndex = (currentPage.value - 1) * countPerPage.value;
|
|
||||||
const lastIndex = currentPage.value * countPerPage.value;
|
|
||||||
|
|
||||||
return transactionData.items.slice(firstIndex, lastIndex);
|
|
||||||
} else {
|
} else {
|
||||||
return currentPageTransactions.value;
|
return [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -874,6 +963,11 @@ const recentDateRangeType = computed<number>({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const queryPageType = computed<number>({
|
||||||
|
get: () => pageType.value,
|
||||||
|
set: (value) => changePageType(value)
|
||||||
|
});
|
||||||
|
|
||||||
const queryType = computed<number>({
|
const queryType = computed<number>({
|
||||||
get: () => query.value.type,
|
get: () => query.value.type,
|
||||||
set: (value) => changeTypeFilter(value)
|
set: (value) => changeTypeFilter(value)
|
||||||
@@ -974,6 +1068,10 @@ const currentMonthTotalAmount = computed<TransactionListDisplayTotalAmount | nul
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function noTransactionInMonthDay(date: Date): boolean {
|
||||||
|
return !currentMonthTransactionData.value || !currentMonthTransactionData.value.dailyTotalAmounts || !currentMonthTransactionData.value.dailyTotalAmounts[getDay(date)];
|
||||||
|
}
|
||||||
|
|
||||||
function getCategoryListItemCheckedClass(category: TransactionCategory, queryCategoryIds: Record<string, boolean>): Record<string, boolean> {
|
function getCategoryListItemCheckedClass(category: TransactionCategory, queryCategoryIds: Record<string, boolean>): Record<string, boolean> {
|
||||||
if (queryCategoryIds && queryCategoryIds[category.id]) {
|
if (queryCategoryIds && queryCategoryIds[category.id]) {
|
||||||
return {
|
return {
|
||||||
@@ -1006,7 +1104,7 @@ function updateUrlWhenChanged(changed: boolean): void {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
currentPageTransactions.value = [];
|
currentPageTransactions.value = [];
|
||||||
transactionsStore.clearTransactions();
|
transactionsStore.clearTransactions();
|
||||||
router.push(`/transaction/list?${transactionsStore.getTransactionListPageParams()}`);
|
router.push(`/transaction/list?${transactionsStore.getTransactionListPageParams(pageType.value)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1036,6 +1134,32 @@ function init(initProps: TransactionListProps): void {
|
|||||||
keyword: initProps.initKeyword || ''
|
keyword: initProps.initKeyword || ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (initProps.initPageType) {
|
||||||
|
const type = TransactionListPageType.valueOf(parseInt(initProps.initPageType));
|
||||||
|
|
||||||
|
if (type) {
|
||||||
|
pageType.value = type.type;
|
||||||
|
currentCalendarDate.value = getMonthFirstDayOrCurrentDayShortDate(query.value.minTime);
|
||||||
|
|
||||||
|
if (pageType.value === TransactionListPageType.Calendar.type) {
|
||||||
|
const dateRange = getFullMonthDateRange(query.value.minTime, query.value.maxTime, firstDayOfWeek.value);
|
||||||
|
|
||||||
|
if (dateRange) {
|
||||||
|
const changed = transactionsStore.updateTransactionListFilter({
|
||||||
|
dateType: dateRange.dateType,
|
||||||
|
maxTime: dateRange.maxTime,
|
||||||
|
minTime: dateRange.minTime
|
||||||
|
});
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
updateUrlWhenChanged(changed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
searchKeyword.value = initProps.initKeyword || '';
|
searchKeyword.value = initProps.initKeyword || '';
|
||||||
currentAmountFilterType.value = '';
|
currentAmountFilterType.value = '';
|
||||||
|
|
||||||
@@ -1108,6 +1232,25 @@ function reload(force: boolean, init: boolean): void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function changePageType(type: number): void {
|
||||||
|
pageType.value = type;
|
||||||
|
currentCalendarDate.value = getMonthFirstDayOrCurrentDayShortDate(query.value.minTime);
|
||||||
|
|
||||||
|
if (pageType.value === TransactionListPageType.Calendar.type) {
|
||||||
|
const dateRange = getFullMonthDateRange(query.value.minTime, query.value.maxTime, firstDayOfWeek.value);
|
||||||
|
|
||||||
|
if (dateRange) {
|
||||||
|
transactionsStore.updateTransactionListFilter({
|
||||||
|
dateType: dateRange.dateType,
|
||||||
|
maxTime: dateRange.maxTime,
|
||||||
|
minTime: dateRange.minTime
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUrlWhenChanged(true);
|
||||||
|
}
|
||||||
|
|
||||||
function changeDateFilter(dateRange: TimeRangeAndDateType | number | null): void {
|
function changeDateFilter(dateRange: TimeRangeAndDateType | number | null): void {
|
||||||
if (dateRange === DateRange.Custom.type || (isObject(dateRange) && dateRange.dateType === DateRange.Custom.type && !dateRange.minTime && !dateRange.maxTime)) { // Custom
|
if (dateRange === DateRange.Custom.type || (isObject(dateRange) && dateRange.dateType === DateRange.Custom.type && !dateRange.minTime && !dateRange.maxTime)) { // Custom
|
||||||
if (!query.value.minTime || !query.value.maxTime) {
|
if (!query.value.minTime || !query.value.maxTime) {
|
||||||
@@ -1134,6 +1277,15 @@ function changeDateFilter(dateRange: TimeRangeAndDateType | number | null): void
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pageType.value === TransactionListPageType.Calendar.type) {
|
||||||
|
currentCalendarDate.value = getMonthFirstDayOrCurrentDayShortDate(dateRange.minTime);
|
||||||
|
const fullMonthDateRange = getFullMonthDateRange(dateRange.minTime, dateRange.maxTime, firstDayOfWeek.value);
|
||||||
|
|
||||||
|
if (fullMonthDateRange) {
|
||||||
|
dateRange = fullMonthDateRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (query.value.dateType === dateRange.dateType && query.value.maxTime === dateRange.maxTime && query.value.minTime === dateRange.minTime) {
|
if (query.value.dateType === dateRange.dateType && query.value.maxTime === dateRange.maxTime && query.value.minTime === dateRange.minTime) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1158,6 +1310,17 @@ function changeCustomDateFilter(minTime: number, maxTime: number): void {
|
|||||||
dateType = getDateTypeByDateRange(minTime, maxTime, firstDayOfWeek.value, DateRangeScene.Normal);
|
dateType = getDateTypeByDateRange(minTime, maxTime, firstDayOfWeek.value, DateRangeScene.Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pageType.value === TransactionListPageType.Calendar.type) {
|
||||||
|
currentCalendarDate.value = getMonthFirstDayOrCurrentDayShortDate(minTime);
|
||||||
|
const dateRange = getFullMonthDateRange(minTime, maxTime, firstDayOfWeek.value);
|
||||||
|
|
||||||
|
if (dateRange) {
|
||||||
|
minTime = dateRange.minTime;
|
||||||
|
maxTime = dateRange.maxTime;
|
||||||
|
dateType = dateRange.dateType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (query.value.dateType === dateType && query.value.maxTime === maxTime && query.value.minTime === minTime) {
|
if (query.value.dateType === dateType && query.value.maxTime === maxTime && query.value.minTime === minTime) {
|
||||||
showCustomDateRangeDialog.value = false;
|
showCustomDateRangeDialog.value = false;
|
||||||
return;
|
return;
|
||||||
@@ -1188,6 +1351,15 @@ function shiftDateRange(startTime: number, endTime: number, scale: number): void
|
|||||||
newDateRange = getShiftedDateRangeAndDateType(startTime, endTime, scale, firstDayOfWeek.value, DateRangeScene.Normal);
|
newDateRange = getShiftedDateRangeAndDateType(startTime, endTime, scale, firstDayOfWeek.value, DateRangeScene.Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pageType.value === TransactionListPageType.Calendar.type) {
|
||||||
|
currentCalendarDate.value = getMonthFirstDayOrCurrentDayShortDate(newDateRange.minTime);
|
||||||
|
const fullMonthDateRange = getFullMonthDateRange(newDateRange.minTime, newDateRange.maxTime, firstDayOfWeek.value);
|
||||||
|
|
||||||
|
if (fullMonthDateRange) {
|
||||||
|
newDateRange = fullMonthDateRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const changed = transactionsStore.updateTransactionListFilter({
|
const changed = transactionsStore.updateTransactionListFilter({
|
||||||
dateType: newDateRange.dateType,
|
dateType: newDateRange.dateType,
|
||||||
maxTime: newDateRange.maxTime,
|
maxTime: newDateRange.maxTime,
|
||||||
@@ -1600,4 +1772,10 @@ init(props);
|
|||||||
.transaction-tag-menu .item-in-multiple-selection span {
|
.transaction-tag-menu .item-in-multiple-selection span {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.transaction-calendar-container .dp__calendar .dp__calendar_row {
|
||||||
|
--dp-cell-size: 80px;
|
||||||
|
--dp-primary-color: rgba(var(--v-theme-primary), var(--v-activated-opacity));
|
||||||
|
--dp-primary-text-color: rgb(var(--v-theme-primary));
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user