From 43154832b6ff3c215ae7ea1e15952b07d7ff88b4 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sat, 3 Jan 2026 22:42:58 +0800 Subject: [PATCH] support filtering geographic location and pictures in insights explorer --- src/core/explorer.ts | 4 + src/lib/services.ts | 2 +- src/models/explorer.ts | 116 ++++++++++++++++++ src/models/transaction.ts | 1 + src/stores/explorer.ts | 3 +- .../insights/tabs/ExplorerQueryTab.vue | 10 ++ 6 files changed, 134 insertions(+), 2 deletions(-) diff --git a/src/core/explorer.ts b/src/core/explorer.ts index f8e150b4..8e00bd37 100644 --- a/src/core/explorer.ts +++ b/src/core/explorer.ts @@ -21,7 +21,9 @@ export enum TransactionExplorerConditionFieldType { DestinationAccount = 'destinationAccount', SourceAmount = 'sourceAmount', DestinationAmount = 'destinationAmount', + GeoLocation = 'geoLocation', TransactionTag = 'transactionTag', + Pictures = 'pictures', Description = 'description' } @@ -35,7 +37,9 @@ export class TransactionExplorerConditionField implements NameValue { public static readonly DestinationAccount = new TransactionExplorerConditionField('Destination Account', TransactionExplorerConditionFieldType.DestinationAccount); public static readonly SourceAmount = new TransactionExplorerConditionField('Amount', TransactionExplorerConditionFieldType.SourceAmount); public static readonly DestinationAmount = new TransactionExplorerConditionField('Transfer In Amount', TransactionExplorerConditionFieldType.DestinationAmount); + public static readonly GeoLocation = new TransactionExplorerConditionField('Geographic Location', TransactionExplorerConditionFieldType.GeoLocation); public static readonly TransactionTag = new TransactionExplorerConditionField('Tags', TransactionExplorerConditionFieldType.TransactionTag); + public static readonly Pictures = new TransactionExplorerConditionField('Pictures', TransactionExplorerConditionFieldType.Pictures); public static readonly Description = new TransactionExplorerConditionField('Description', TransactionExplorerConditionFieldType.Description); public readonly name: string; diff --git a/src/lib/services.ts b/src/lib/services.ts index 75d7accf..35423c74 100644 --- a/src/lib/services.ts +++ b/src/lib/services.ts @@ -509,7 +509,7 @@ export default { return axios.get>(`v1/transactions/list/by_month.json?year=${req.year}&month=${req.month}&type=${req.type}&category_ids=${req.categoryIds}&account_ids=${req.accountIds}&tag_filter=${tagFilter}&amount_filter=${amountFilter}&keyword=${keyword}&trim_account=true&trim_category=true&trim_tag=true`); }, getAllTransactions: (req: TransactionAllListRequest): ApiResponsePromise => { - return axios.get>(`v1/transactions/list/all.json?trim_account=true&trim_category=true&trim_tag=true&start_time=${req.startTime}&end_time=${req.endTime}`); + return axios.get>(`v1/transactions/list/all.json?trim_account=true&with_pictures=${!!req.withPictures}&trim_category=true&trim_tag=true&start_time=${req.startTime}&end_time=${req.endTime}`); }, getReconciliationStatements: (req: TransactionReconciliationStatementRequest): ApiResponsePromise => { return axios.get>(`v1/transactions/reconciliation_statements.json?account_id=${req.accountId}&start_time=${req.startTime}&end_time=${req.endTime}`); diff --git a/src/models/explorer.ts b/src/models/explorer.ts index 3d04f1bc..973c0f18 100644 --- a/src/models/explorer.ts +++ b/src/models/explorer.ts @@ -51,9 +51,15 @@ export class TransactionExplorerQuery { case TransactionExplorerConditionField.DestinationAmount: condition = new TransactionExplorerDestinationAmountCondition(TransactionExplorerConditionOperatorType.Between, [0, 0]); break; + case TransactionExplorerConditionField.GeoLocation: + condition = new TransactionExplorerGeoLocationCondition(TransactionExplorerConditionOperatorType.IsNotEmpty, []); + break; case TransactionExplorerConditionField.TransactionTag: condition = new TransactionExplorerTransactionTagCondition(TransactionExplorerConditionOperatorType.HasAny, []); break; + case TransactionExplorerConditionField.Pictures: + condition = new TransactionExplorerPicturesCondition(TransactionExplorerConditionOperatorType.IsNotEmpty, []); + break; case TransactionExplorerConditionField.Description: condition = new TransactionExplorerDescriptionCondition(TransactionExplorerConditionOperatorType.Contains, ''); break; @@ -302,9 +308,15 @@ export class TransactionExplorerConditionWithRelation { case TransactionExplorerConditionField.DestinationAmount.value: operatorTypes = TransactionExplorerDestinationAmountCondition.supportedOperators; break; + case TransactionExplorerConditionField.GeoLocation.value: + operatorTypes = TransactionExplorerGeoLocationCondition.supportedOperators; + break; case TransactionExplorerConditionField.TransactionTag.value: operatorTypes = TransactionExplorerTransactionTagCondition.supportedOperators; break; + case TransactionExplorerConditionField.Pictures.value: + operatorTypes = TransactionExplorerPicturesCondition.supportedOperators; + break; case TransactionExplorerConditionField.Description.value: operatorTypes = TransactionExplorerDescriptionCondition.supportedOperators; break; @@ -385,11 +397,21 @@ export class TransactionExplorerConditionWithRelation { condition = new TransactionExplorerDestinationAmountCondition(conditionOperator as AmountConditionOperator, conditionValue as [number, number]); } break; + case TransactionExplorerConditionField.GeoLocation.value: + if (TransactionExplorerGeoLocationCondition.supportedOperators[conditionOperator] && Array.isArray(conditionValue)) { + condition = new TransactionExplorerGeoLocationCondition(conditionOperator as GeoLocationConditionOperator, conditionValue as string[]); + } + break; case TransactionExplorerConditionField.TransactionTag.value: if (TransactionExplorerTransactionTagCondition.supportedOperators[conditionOperator] && Array.isArray(conditionValue)) { condition = new TransactionExplorerTransactionTagCondition(conditionOperator as TransactionTagConditionOperator, conditionValue as string[]); } break; + case TransactionExplorerConditionField.Pictures.value: + if (TransactionExplorerPicturesCondition.supportedOperators[conditionOperator] && Array.isArray(conditionValue)) { + condition = new TransactionExplorerPicturesCondition(conditionOperator as PicturesConditionOperator, conditionValue as string[]); + } + break; case TransactionExplorerConditionField.Description.value: if (TransactionExplorerDescriptionCondition.supportedOperators[conditionOperator] && typeof conditionValue === 'string') { condition = new TransactionExplorerDescriptionCondition(conditionOperator as DescriptionConditionOperator, conditionValue); @@ -690,6 +712,53 @@ export class TransactionExplorerDestinationAmountCondition extends AbstractTrans } } +type GeoLocationConditionOperator = TransactionExplorerConditionOperatorType.IsEmpty | + TransactionExplorerConditionOperatorType.IsNotEmpty; + +export class TransactionExplorerGeoLocationCondition implements TransactionExplorerCondition { + public static readonly supportedOperators: PartialRecord = { + [TransactionExplorerConditionOperatorType.IsEmpty]: true, + [TransactionExplorerConditionOperatorType.IsNotEmpty]: true + }; + + public readonly field = TransactionExplorerConditionFieldType.GeoLocation; + public readonly operator: GeoLocationConditionOperator = TransactionExplorerConditionOperatorType.IsNotEmpty; + public value: string[]; + + constructor(operator: GeoLocationConditionOperator, value: string[]) { + this.operator = operator; + this.value = value; + } + + public getValueForStore(): string[] { + if (this.operator === TransactionExplorerConditionOperatorType.IsEmpty || this.operator === TransactionExplorerConditionOperatorType.IsNotEmpty) { + return []; + } + + return []; + } + + public match(transaction: TransactionInsightDataItem): boolean { + if (this.operator === TransactionExplorerConditionOperatorType.IsEmpty) { + return !transaction.geoLocation; + } else if (this.operator === TransactionExplorerConditionOperatorType.IsNotEmpty) { + return !!transaction.geoLocation; + } + + return false; + } + + public toExpression(): string { + if (this.operator === TransactionExplorerConditionOperatorType.IsEmpty) { + return `geo_location IS EMPTY`; + } else if (this.operator === TransactionExplorerConditionOperatorType.IsNotEmpty) { + return `geo_location IS NOT EMPTY`; + } + + return ''; + } +} + type TransactionTagConditionOperator = TransactionExplorerConditionOperatorType.IsEmpty | TransactionExplorerConditionOperatorType.IsNotEmpty | TransactionExplorerConditionOperatorType.Equals | @@ -825,6 +894,53 @@ export class TransactionExplorerTransactionTagCondition implements TransactionEx } } +type PicturesConditionOperator = TransactionExplorerConditionOperatorType.IsEmpty | + TransactionExplorerConditionOperatorType.IsNotEmpty; + +export class TransactionExplorerPicturesCondition implements TransactionExplorerCondition { + public static readonly supportedOperators: PartialRecord = { + [TransactionExplorerConditionOperatorType.IsEmpty]: true, + [TransactionExplorerConditionOperatorType.IsNotEmpty]: true + }; + + public readonly field = TransactionExplorerConditionFieldType.Pictures; + public readonly operator: PicturesConditionOperator = TransactionExplorerConditionOperatorType.IsNotEmpty; + public value: string[]; + + constructor(operator: PicturesConditionOperator, value: string[]) { + this.operator = operator; + this.value = value; + } + + public getValueForStore(): string[] { + if (this.operator === TransactionExplorerConditionOperatorType.IsEmpty || this.operator === TransactionExplorerConditionOperatorType.IsNotEmpty) { + return []; + } + + return []; + } + + public match(transaction: TransactionInsightDataItem): boolean { + if (this.operator === TransactionExplorerConditionOperatorType.IsEmpty) { + return !transaction.pictures || transaction.pictures.length < 1; + } else if (this.operator === TransactionExplorerConditionOperatorType.IsNotEmpty) { + return !!transaction.pictures && transaction.pictures.length > 0; + } + + return false; + } + + public toExpression(): string { + if (this.operator === TransactionExplorerConditionOperatorType.IsEmpty) { + return `pictures IS EMPTY`; + } else if (this.operator === TransactionExplorerConditionOperatorType.IsNotEmpty) { + return `pictures IS NOT EMPTY`; + } + + return ''; + } +} + type DescriptionConditionOperator = TransactionExplorerConditionOperatorType.IsEmpty | TransactionExplorerConditionOperatorType.IsNotEmpty | TransactionExplorerConditionOperatorType.Equals | diff --git a/src/models/transaction.ts b/src/models/transaction.ts index 164be8b8..ba805f0e 100644 --- a/src/models/transaction.ts +++ b/src/models/transaction.ts @@ -600,6 +600,7 @@ export interface TransactionListInMonthByPageRequest { export interface TransactionAllListRequest { readonly startTime: number; readonly endTime: number; + readonly withPictures?: boolean; } export interface TransactionReconciliationStatementRequest { diff --git a/src/stores/explorer.ts b/src/stores/explorer.ts index f56241f7..bf8ff2b8 100644 --- a/src/stores/explorer.ts +++ b/src/stores/explorer.ts @@ -857,7 +857,8 @@ export const useExplorersStore = defineStore('explorers', () => { return new Promise((resolve, reject) => { services.getAllTransactions({ startTime: transactionExplorerFilter.value.startTime, - endTime: transactionExplorerFilter.value.endTime + endTime: transactionExplorerFilter.value.endTime, + withPictures: true }).then(response => { const data = response.data; diff --git a/src/views/desktop/insights/tabs/ExplorerQueryTab.vue b/src/views/desktop/insights/tabs/ExplorerQueryTab.vue index 7fa7f37f..8042f6ba 100644 --- a/src/views/desktop/insights/tabs/ExplorerQueryTab.vue +++ b/src/views/desktop/insights/tabs/ExplorerQueryTab.vue @@ -221,6 +221,11 @@ /> + +
+ +