support canceling AI image recognition
This commit is contained in:
@@ -29,7 +29,7 @@
|
|||||||
import { ref, useTemplateRef } from 'vue';
|
import { ref, useTemplateRef } from 'vue';
|
||||||
|
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
import { useI18nUIComponents, showLoading, hideLoading } from '@/lib/ui/mobile.ts';
|
import { useI18nUIComponents, closeAllDialog } from '@/lib/ui/mobile.ts';
|
||||||
|
|
||||||
import { useTransactionsStore } from '@/stores/transaction.ts';
|
import { useTransactionsStore } from '@/stores/transaction.ts';
|
||||||
|
|
||||||
@@ -38,6 +38,7 @@ import { SUPPORTED_IMAGE_EXTENSIONS } from '@/consts/file.ts';
|
|||||||
|
|
||||||
import type { RecognizedReceiptImageResponse } from '@/models/large_language_model.ts';
|
import type { RecognizedReceiptImageResponse } from '@/models/large_language_model.ts';
|
||||||
|
|
||||||
|
import { generateRandomUUID } from '@/lib/misc.ts';
|
||||||
import { compressJpgImage } from '@/lib/ui/common.ts';
|
import { compressJpgImage } from '@/lib/ui/common.ts';
|
||||||
import logger from '@/lib/logger.ts';
|
import logger from '@/lib/logger.ts';
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ const emit = defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { tt } = useI18n();
|
const { tt } = useI18n();
|
||||||
const { showToast } = useI18nUIComponents();
|
const { showCancelableLoading, showToast } = useI18nUIComponents();
|
||||||
|
|
||||||
const transactionsStore = useTransactionsStore();
|
const transactionsStore = useTransactionsStore();
|
||||||
|
|
||||||
@@ -59,6 +60,7 @@ const imageInput = useTemplateRef<HTMLInputElement>('imageInput');
|
|||||||
|
|
||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
const recognizing = ref<boolean>(false);
|
const recognizing = ref<boolean>(false);
|
||||||
|
const cancelRecognizingUuid = ref<string | undefined>(undefined);
|
||||||
const imageFile = ref<File | null>(null);
|
const imageFile = ref<File | null>(null);
|
||||||
const imageSrc = ref<string | undefined>(undefined);
|
const imageSrc = ref<string | undefined>(undefined);
|
||||||
|
|
||||||
@@ -105,19 +107,27 @@ function confirm(): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancelRecognizingUuid.value = generateRandomUUID();
|
||||||
recognizing.value = true;
|
recognizing.value = true;
|
||||||
showLoading(() => recognizing.value);
|
showCancelableLoading('Recognizing...', 'Cancel Recognition', cancelRecognize);
|
||||||
|
|
||||||
transactionsStore.recognizeReceiptImage({
|
transactionsStore.recognizeReceiptImage({
|
||||||
imageFile: imageFile.value
|
imageFile: imageFile.value,
|
||||||
|
cancelableUuid: cancelRecognizingUuid.value
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
recognizing.value = false;
|
recognizing.value = false;
|
||||||
hideLoading();
|
cancelRecognizingUuid.value = undefined;
|
||||||
|
closeAllDialog();
|
||||||
emit('update:show', false);
|
emit('update:show', false);
|
||||||
emit('recognition:change', response);
|
emit('recognition:change', response);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
if (error.canceled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
recognizing.value = false;
|
recognizing.value = false;
|
||||||
hideLoading();
|
cancelRecognizingUuid.value = undefined;
|
||||||
|
closeAllDialog();
|
||||||
|
|
||||||
if (!error.processed) {
|
if (!error.processed) {
|
||||||
showToast(error.message || error);
|
showToast(error.message || error);
|
||||||
@@ -125,6 +135,19 @@ function confirm(): void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cancelRecognize(): void {
|
||||||
|
if (!cancelRecognizingUuid.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionsStore.cancelRecognizeReceiptImage(cancelRecognizingUuid.value);
|
||||||
|
recognizing.value = false;
|
||||||
|
cancelRecognizingUuid.value = undefined;
|
||||||
|
closeAllDialog();
|
||||||
|
|
||||||
|
showToast('User Canceled');
|
||||||
|
}
|
||||||
|
|
||||||
function cancel(): void {
|
function cancel(): void {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
@@ -133,6 +156,7 @@ function close(): void {
|
|||||||
emit('update:show', false);
|
emit('update:show', false);
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
recognizing.value = false;
|
recognizing.value = false;
|
||||||
|
cancelRecognizingUuid.value = undefined;
|
||||||
imageFile.value = null;
|
imageFile.value = null;
|
||||||
imageSrc.value = undefined;
|
imageSrc.value = undefined;
|
||||||
}
|
}
|
||||||
@@ -140,6 +164,7 @@ function close(): void {
|
|||||||
function onSheetOpen(): void {
|
function onSheetOpen(): void {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
recognizing.value = false;
|
recognizing.value = false;
|
||||||
|
cancelRecognizingUuid.value = undefined;
|
||||||
imageFile.value = null;
|
imageFile.value = null;
|
||||||
imageSrc.value = undefined;
|
imageSrc.value = undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-3
@@ -159,6 +159,7 @@ import {
|
|||||||
import { getTimezoneOffsetMinutes } from './datetime.ts';
|
import { getTimezoneOffsetMinutes } from './datetime.ts';
|
||||||
import { generateRandomUUID } from './misc.ts';
|
import { generateRandomUUID } from './misc.ts';
|
||||||
import { getBasePath } from './web.ts';
|
import { getBasePath } from './web.ts';
|
||||||
|
import logger from './logger.ts';
|
||||||
|
|
||||||
interface ApiRequestConfig extends AxiosRequestConfig {
|
interface ApiRequestConfig extends AxiosRequestConfig {
|
||||||
readonly headers: AxiosRequestHeaders;
|
readonly headers: AxiosRequestHeaders;
|
||||||
@@ -166,12 +167,14 @@ interface ApiRequestConfig extends AxiosRequestConfig {
|
|||||||
readonly ignoreBlocked?: boolean;
|
readonly ignoreBlocked?: boolean;
|
||||||
readonly ignoreError?: boolean;
|
readonly ignoreError?: boolean;
|
||||||
readonly timeout?: number;
|
readonly timeout?: number;
|
||||||
|
readonly cancelableUuid?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiResponsePromise<T> = Promise<AxiosResponse<ApiResponse<T>>>;
|
export type ApiResponsePromise<T> = Promise<AxiosResponse<ApiResponse<T>>>;
|
||||||
|
|
||||||
let needBlockRequest = false;
|
let needBlockRequest = false;
|
||||||
const blockedRequests: ((token: string | undefined) => void)[] = [];
|
const blockedRequests: ((token: string | undefined) => void)[] = [];
|
||||||
|
const cancelableRequests: Record<string, boolean> = {};
|
||||||
|
|
||||||
axios.defaults.baseURL = getBasePath() + BASE_API_URL_PATH;
|
axios.defaults.baseURL = getBasePath() + BASE_API_URL_PATH;
|
||||||
axios.defaults.timeout = DEFAULT_API_TIMEOUT;
|
axios.defaults.timeout = DEFAULT_API_TIMEOUT;
|
||||||
@@ -202,8 +205,20 @@ axios.interceptors.request.use((config: ApiRequestConfig) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
axios.interceptors.response.use(response => {
|
axios.interceptors.response.use(response => {
|
||||||
|
if ('cancelableUuid' in response.config && response.config.cancelableUuid && cancelableRequests[response.config.cancelableUuid as string]) {
|
||||||
|
logger.debug('Response canceled by user request, url: ' + response.config.url + ', cancelableUuid: ' + response.config.cancelableUuid);
|
||||||
|
delete cancelableRequests[response.config.cancelableUuid as string];
|
||||||
|
return Promise.reject({ canceled: true });
|
||||||
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}, error => {
|
}, error => {
|
||||||
|
if ('cancelableUuid' in error.response.config && error.response.config.cancelableUuid && cancelableRequests[error.response.config.cancelableUuid]) {
|
||||||
|
logger.debug('Response canceled by user request, url: ' + error.response.config.url + ', cancelableUuid: ' + error.response.config.cancelableUuid);
|
||||||
|
delete cancelableRequests[error.response.config.cancelableUuid];
|
||||||
|
return Promise.reject({ canceled: true });
|
||||||
|
}
|
||||||
|
|
||||||
if (error.response && !error.response.config.ignoreError && error.response.data && error.response.data.errorCode) {
|
if (error.response && !error.response.config.ignoreError && error.response.data && error.response.data.errorCode) {
|
||||||
const errorCode = error.response.data.errorCode;
|
const errorCode = error.response.data.errorCode;
|
||||||
|
|
||||||
@@ -650,12 +665,13 @@ export default {
|
|||||||
deleteTransactionTemplate: (req: TransactionTemplateDeleteRequest): ApiResponsePromise<boolean> => {
|
deleteTransactionTemplate: (req: TransactionTemplateDeleteRequest): ApiResponsePromise<boolean> => {
|
||||||
return axios.post<ApiResponse<boolean>>('v1/transaction/templates/delete.json', req);
|
return axios.post<ApiResponse<boolean>>('v1/transaction/templates/delete.json', req);
|
||||||
},
|
},
|
||||||
recognizeReceiptImage: ({ imageFile }: { imageFile: File }): ApiResponsePromise<RecognizedReceiptImageResponse> => {
|
recognizeReceiptImage: ({ imageFile, cancelableUuid }: { imageFile: File, cancelableUuid?: string }): ApiResponsePromise<RecognizedReceiptImageResponse> => {
|
||||||
return axios.postForm<ApiResponse<RecognizedReceiptImageResponse>>('v1/llm/transactions/recognize_receipt_image.json', {
|
return axios.postForm<ApiResponse<RecognizedReceiptImageResponse>>('v1/llm/transactions/recognize_receipt_image.json', {
|
||||||
image: imageFile
|
image: imageFile
|
||||||
}, {
|
}, {
|
||||||
timeout: DEFAULT_LLM_API_TIMEOUT
|
timeout: DEFAULT_LLM_API_TIMEOUT,
|
||||||
});
|
cancelableUuid: cancelableUuid
|
||||||
|
} as ApiRequestConfig);
|
||||||
},
|
},
|
||||||
getLatestExchangeRates: (param: { ignoreError?: boolean }): ApiResponsePromise<LatestExchangeRateResponse> => {
|
getLatestExchangeRates: (param: { ignoreError?: boolean }): ApiResponsePromise<LatestExchangeRateResponse> => {
|
||||||
return axios.get<ApiResponse<LatestExchangeRateResponse>>('v1/exchange_rates/latest.json', {
|
return axios.get<ApiResponse<LatestExchangeRateResponse>>('v1/exchange_rates/latest.json', {
|
||||||
@@ -672,6 +688,9 @@ export default {
|
|||||||
getServerVersion: (): ApiResponsePromise<VersionInfo> => {
|
getServerVersion: (): ApiResponsePromise<VersionInfo> => {
|
||||||
return axios.get<ApiResponse<VersionInfo>>('v1/systems/version.json');
|
return axios.get<ApiResponse<VersionInfo>>('v1/systems/version.json');
|
||||||
},
|
},
|
||||||
|
cancelRequest: (cancelableUuid: string) => {
|
||||||
|
cancelableRequests[cancelableUuid] = true;
|
||||||
|
},
|
||||||
generateQrCodeUrl: (qrCodeName: string): string => {
|
generateQrCodeUrl: (qrCodeName: string): string => {
|
||||||
return `${getBasePath()}${BASE_QRCODE_PATH}/${qrCodeName}.png`;
|
return `${getBasePath()}${BASE_QRCODE_PATH}/${qrCodeName}.png`;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ export function hideLoading(): void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function closeAllDialog(): void {
|
||||||
|
f7ready((f7) => {
|
||||||
|
return f7.dialog.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function createInlinePicker(containerEl: string, inputEl: string, cols: Picker.ColumnParameters[], value: string[], events?: { change: (picker: Picker.Picker, value: unknown, displayValue: unknown) => void }): Picker.Picker {
|
export function createInlinePicker(containerEl: string, inputEl: string, cols: Picker.ColumnParameters[], value: string[], events?: { change: (picker: Picker.Picker, value: unknown, displayValue: unknown) => void }): Picker.Picker {
|
||||||
return f7.picker.create({
|
return f7.picker.create({
|
||||||
containerEl: containerEl,
|
containerEl: containerEl,
|
||||||
@@ -282,6 +288,27 @@ export function useI18nUIComponents() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showCancelableLoading(message: string, cancelButtonText: string, cancelCallback?: (dialog: Dialog.Dialog, e: Event) => void): void {
|
||||||
|
const cancelButton: Dialog.DialogButton = {
|
||||||
|
text: tt(cancelButtonText),
|
||||||
|
onClick: (dialog, event) => {
|
||||||
|
if (cancelCallback) {
|
||||||
|
cancelCallback(dialog, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
f7ready((f7) => {
|
||||||
|
f7.dialog.create({
|
||||||
|
title: tt(message),
|
||||||
|
content: `<div class="preloader"><span class="preloader-inner">${[0, 1, 2, 3, 4, 5, 6, 7].map(() => '<span class="preloader-inner-line"></span>').join('')}</span></div>`,
|
||||||
|
cssClass: 'dialog-preloader',
|
||||||
|
animate: isEnableAnimate(),
|
||||||
|
buttons: [cancelButton]
|
||||||
|
}).open();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function showToast(message: string, timeout?: number): void {
|
function showToast(message: string, timeout?: number): void {
|
||||||
f7ready((f7) => {
|
f7ready((f7) => {
|
||||||
f7.toast.create({
|
f7.toast.create({
|
||||||
@@ -296,6 +323,7 @@ export function useI18nUIComponents() {
|
|||||||
showAlert: showAlert,
|
showAlert: showAlert,
|
||||||
showConfirm: showConfirm,
|
showConfirm: showConfirm,
|
||||||
showPrompt: showPrompt,
|
showPrompt: showPrompt,
|
||||||
|
showCancelableLoading: showCancelableLoading,
|
||||||
showToast: showToast,
|
showToast: showToast,
|
||||||
routeBackOnError
|
routeBackOnError
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "Löschen",
|
"Clear": "Löschen",
|
||||||
"Generate": "Generate",
|
"Generate": "Generate",
|
||||||
"Recognize": "Recognize",
|
"Recognize": "Recognize",
|
||||||
|
"Recognizing...": "Recognizing...",
|
||||||
|
"Cancel Recognition": "Cancel Recognition",
|
||||||
"None": "Keine",
|
"None": "Keine",
|
||||||
"Unspecified": "Nicht angegeben",
|
"Unspecified": "Nicht angegeben",
|
||||||
"Not set": "Nicht festgelegt",
|
"Not set": "Nicht festgelegt",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "Anzeigereihenfolge speichern",
|
"Save Display Order": "Anzeigereihenfolge speichern",
|
||||||
"Change Language": "Sprache ändern",
|
"Change Language": "Sprache ändern",
|
||||||
"Date is too early": "Datum ist zu früh",
|
"Date is too early": "Datum ist zu früh",
|
||||||
|
"User Canceled": "User Canceled",
|
||||||
"Welcome to ezBookkeeping": "Willkommen bei ezBookkeeping",
|
"Welcome to ezBookkeeping": "Willkommen bei ezBookkeeping",
|
||||||
"Please log in with your ezBookkeeping account": "Bitte melden Sie sich mit Ihrem ezBookkeeping-Konto an",
|
"Please log in with your ezBookkeeping account": "Bitte melden Sie sich mit Ihrem ezBookkeeping-Konto an",
|
||||||
"Unlock Application": "Anwendung entsperren",
|
"Unlock Application": "Anwendung entsperren",
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "Clear",
|
"Clear": "Clear",
|
||||||
"Generate": "Generate",
|
"Generate": "Generate",
|
||||||
"Recognize": "Recognize",
|
"Recognize": "Recognize",
|
||||||
|
"Recognizing...": "Recognizing...",
|
||||||
|
"Cancel Recognition": "Cancel Recognition",
|
||||||
"None": "None",
|
"None": "None",
|
||||||
"Unspecified": "Unspecified",
|
"Unspecified": "Unspecified",
|
||||||
"Not set": "Not set",
|
"Not set": "Not set",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "Save Display Order",
|
"Save Display Order": "Save Display Order",
|
||||||
"Change Language": "Change Language",
|
"Change Language": "Change Language",
|
||||||
"Date is too early": "Date is too early",
|
"Date is too early": "Date is too early",
|
||||||
|
"User Canceled": "User Canceled",
|
||||||
"Welcome to ezBookkeeping": "Welcome to ezBookkeeping",
|
"Welcome to ezBookkeeping": "Welcome to ezBookkeeping",
|
||||||
"Please log in with your ezBookkeeping account": "Please log in with your ezBookkeeping account",
|
"Please log in with your ezBookkeeping account": "Please log in with your ezBookkeeping account",
|
||||||
"Unlock Application": "Unlock Application",
|
"Unlock Application": "Unlock Application",
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "Claro",
|
"Clear": "Claro",
|
||||||
"Generate": "Generate",
|
"Generate": "Generate",
|
||||||
"Recognize": "Recognize",
|
"Recognize": "Recognize",
|
||||||
|
"Recognizing...": "Recognizing...",
|
||||||
|
"Cancel Recognition": "Cancel Recognition",
|
||||||
"None": "Ninguno",
|
"None": "Ninguno",
|
||||||
"Unspecified": "No especificado",
|
"Unspecified": "No especificado",
|
||||||
"Not set": "No establecido",
|
"Not set": "No establecido",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "Guardar orden de visualización",
|
"Save Display Order": "Guardar orden de visualización",
|
||||||
"Change Language": "Cambiar idioma",
|
"Change Language": "Cambiar idioma",
|
||||||
"Date is too early": "La fecha es demasiado temprana",
|
"Date is too early": "La fecha es demasiado temprana",
|
||||||
|
"User Canceled": "User Canceled",
|
||||||
"Welcome to ezBookkeeping": "Bienvenido a ezBookkeeping",
|
"Welcome to ezBookkeeping": "Bienvenido a ezBookkeeping",
|
||||||
"Please log in with your ezBookkeeping account": "Inicie sesión con su cuenta de ezBookkeeping",
|
"Please log in with your ezBookkeeping account": "Inicie sesión con su cuenta de ezBookkeeping",
|
||||||
"Unlock Application": "Desbloquear aplicación",
|
"Unlock Application": "Desbloquear aplicación",
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "Effacer",
|
"Clear": "Effacer",
|
||||||
"Generate": "Générer",
|
"Generate": "Générer",
|
||||||
"Recognize": "Reconnaître",
|
"Recognize": "Reconnaître",
|
||||||
|
"Recognizing...": "Recognizing...",
|
||||||
|
"Cancel Recognition": "Cancel Recognition",
|
||||||
"None": "Aucun",
|
"None": "Aucun",
|
||||||
"Unspecified": "Non spécifié",
|
"Unspecified": "Non spécifié",
|
||||||
"Not set": "Non défini",
|
"Not set": "Non défini",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "Enregistrer l'ordre d'affichage",
|
"Save Display Order": "Enregistrer l'ordre d'affichage",
|
||||||
"Change Language": "Changer de langue",
|
"Change Language": "Changer de langue",
|
||||||
"Date is too early": "La date est trop ancienne",
|
"Date is too early": "La date est trop ancienne",
|
||||||
|
"User Canceled": "User Canceled",
|
||||||
"Welcome to ezBookkeeping": "Bienvenue dans ezBookkeeping",
|
"Welcome to ezBookkeeping": "Bienvenue dans ezBookkeeping",
|
||||||
"Please log in with your ezBookkeeping account": "Veuillez vous connecter avec votre compte ezBookkeeping",
|
"Please log in with your ezBookkeeping account": "Veuillez vous connecter avec votre compte ezBookkeeping",
|
||||||
"Unlock Application": "Déverrouiller l'application",
|
"Unlock Application": "Déverrouiller l'application",
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "Pulisci",
|
"Clear": "Pulisci",
|
||||||
"Generate": "Generate",
|
"Generate": "Generate",
|
||||||
"Recognize": "Recognize",
|
"Recognize": "Recognize",
|
||||||
|
"Recognizing...": "Recognizing...",
|
||||||
|
"Cancel Recognition": "Cancel Recognition",
|
||||||
"None": "Nessuno",
|
"None": "Nessuno",
|
||||||
"Unspecified": "Non specificato",
|
"Unspecified": "Non specificato",
|
||||||
"Not set": "Non impostato",
|
"Not set": "Non impostato",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "Salva ordine di visualizzazione",
|
"Save Display Order": "Salva ordine di visualizzazione",
|
||||||
"Change Language": "Cambia lingua",
|
"Change Language": "Cambia lingua",
|
||||||
"Date is too early": "Data troppo anticipata",
|
"Date is too early": "Data troppo anticipata",
|
||||||
|
"User Canceled": "User Canceled",
|
||||||
"Welcome to ezBookkeeping": "Benvenuto in ezBookkeeping",
|
"Welcome to ezBookkeeping": "Benvenuto in ezBookkeeping",
|
||||||
"Please log in with your ezBookkeeping account": "Accedi con il tuo account ezBookkeeping",
|
"Please log in with your ezBookkeeping account": "Accedi con il tuo account ezBookkeeping",
|
||||||
"Unlock Application": "Sblocca applicazione",
|
"Unlock Application": "Sblocca applicazione",
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "消去",
|
"Clear": "消去",
|
||||||
"Generate": "Generate",
|
"Generate": "Generate",
|
||||||
"Recognize": "Recognize",
|
"Recognize": "Recognize",
|
||||||
|
"Recognizing...": "Recognizing...",
|
||||||
|
"Cancel Recognition": "Cancel Recognition",
|
||||||
"None": "なし",
|
"None": "なし",
|
||||||
"Unspecified": "不特定",
|
"Unspecified": "不特定",
|
||||||
"Not set": "セットしていない",
|
"Not set": "セットしていない",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "表示順の保存",
|
"Save Display Order": "表示順の保存",
|
||||||
"Change Language": "言語の変更",
|
"Change Language": "言語の変更",
|
||||||
"Date is too early": "日付が早すぎます",
|
"Date is too early": "日付が早すぎます",
|
||||||
|
"User Canceled": "User Canceled",
|
||||||
"Welcome to ezBookkeeping": "ezBookkeepingへようこそ",
|
"Welcome to ezBookkeeping": "ezBookkeepingへようこそ",
|
||||||
"Please log in with your ezBookkeeping account": "ezBookkeepingアカウントにログインしてください",
|
"Please log in with your ezBookkeeping account": "ezBookkeepingアカウントにログインしてください",
|
||||||
"Unlock Application": "アプリのロックを解除",
|
"Unlock Application": "アプリのロックを解除",
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "Wissen",
|
"Clear": "Wissen",
|
||||||
"Generate": "Genereren",
|
"Generate": "Genereren",
|
||||||
"Recognize": "Recognize",
|
"Recognize": "Recognize",
|
||||||
|
"Recognizing...": "Recognizing...",
|
||||||
|
"Cancel Recognition": "Cancel Recognition",
|
||||||
"None": "Geen",
|
"None": "Geen",
|
||||||
"Unspecified": "Niet gespecificeerd",
|
"Unspecified": "Niet gespecificeerd",
|
||||||
"Not set": "Niet ingesteld",
|
"Not set": "Niet ingesteld",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "Weergavevolgorde opslaan",
|
"Save Display Order": "Weergavevolgorde opslaan",
|
||||||
"Change Language": "Taal wijzigen",
|
"Change Language": "Taal wijzigen",
|
||||||
"Date is too early": "Datum is te vroeg",
|
"Date is too early": "Datum is te vroeg",
|
||||||
|
"User Canceled": "User Canceled",
|
||||||
"Welcome to ezBookkeeping": "Welkom bij ezBookkeeping",
|
"Welcome to ezBookkeeping": "Welkom bij ezBookkeeping",
|
||||||
"Please log in with your ezBookkeeping account": "Log in met je ezBookkeeping-account",
|
"Please log in with your ezBookkeeping account": "Log in met je ezBookkeeping-account",
|
||||||
"Unlock Application": "Applicatie ontgrendelen",
|
"Unlock Application": "Applicatie ontgrendelen",
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "Limpar",
|
"Clear": "Limpar",
|
||||||
"Generate": "Generate",
|
"Generate": "Generate",
|
||||||
"Recognize": "Recognize",
|
"Recognize": "Recognize",
|
||||||
|
"Recognizing...": "Recognizing...",
|
||||||
|
"Cancel Recognition": "Cancel Recognition",
|
||||||
"None": "Nenhum",
|
"None": "Nenhum",
|
||||||
"Unspecified": "Não especificado",
|
"Unspecified": "Não especificado",
|
||||||
"Not set": "Não definido",
|
"Not set": "Não definido",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "Salvar Ordem de Exibição",
|
"Save Display Order": "Salvar Ordem de Exibição",
|
||||||
"Change Language": "Alterar Idioma",
|
"Change Language": "Alterar Idioma",
|
||||||
"Date is too early": "Data é muito cedo",
|
"Date is too early": "Data é muito cedo",
|
||||||
|
"User Canceled": "User Canceled",
|
||||||
"Welcome to ezBookkeeping": "Bem-vindo ao ezBookkeeping",
|
"Welcome to ezBookkeeping": "Bem-vindo ao ezBookkeeping",
|
||||||
"Please log in with your ezBookkeeping account": "Por favor, faça login com sua conta ezBookkeeping",
|
"Please log in with your ezBookkeeping account": "Por favor, faça login com sua conta ezBookkeeping",
|
||||||
"Unlock Application": "Desbloquear Aplicativo",
|
"Unlock Application": "Desbloquear Aplicativo",
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "Очистить",
|
"Clear": "Очистить",
|
||||||
"Generate": "Generate",
|
"Generate": "Generate",
|
||||||
"Recognize": "Recognize",
|
"Recognize": "Recognize",
|
||||||
|
"Recognizing...": "Recognizing...",
|
||||||
|
"Cancel Recognition": "Cancel Recognition",
|
||||||
"None": "Нет",
|
"None": "Нет",
|
||||||
"Unspecified": "Не указано",
|
"Unspecified": "Не указано",
|
||||||
"Not set": "Не установлено",
|
"Not set": "Не установлено",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "Сохранить порядок отображения",
|
"Save Display Order": "Сохранить порядок отображения",
|
||||||
"Change Language": "Изменить язык",
|
"Change Language": "Изменить язык",
|
||||||
"Date is too early": "Дата слишком ранняя",
|
"Date is too early": "Дата слишком ранняя",
|
||||||
|
"User Canceled": "User Canceled",
|
||||||
"Welcome to ezBookkeeping": "Добро пожаловать в ezBookkeeping",
|
"Welcome to ezBookkeeping": "Добро пожаловать в ezBookkeeping",
|
||||||
"Please log in with your ezBookkeeping account": "Пожалуйста, войдите в свою учетную запись ezBookkeeping",
|
"Please log in with your ezBookkeeping account": "Пожалуйста, войдите в свою учетную запись ezBookkeeping",
|
||||||
"Unlock Application": "Разблокировать приложение",
|
"Unlock Application": "Разблокировать приложение",
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "ล้าง",
|
"Clear": "ล้าง",
|
||||||
"Generate": "สร้าง",
|
"Generate": "สร้าง",
|
||||||
"Recognize": "จดจำ",
|
"Recognize": "จดจำ",
|
||||||
|
"Recognizing...": "Recognizing...",
|
||||||
|
"Cancel Recognition": "Cancel Recognition",
|
||||||
"None": "ไม่มี",
|
"None": "ไม่มี",
|
||||||
"Unspecified": "ไม่ระบุ",
|
"Unspecified": "ไม่ระบุ",
|
||||||
"Not set": "ยังไม่ได้ตั้งค่า",
|
"Not set": "ยังไม่ได้ตั้งค่า",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "บันทึกลำดับการแสดง",
|
"Save Display Order": "บันทึกลำดับการแสดง",
|
||||||
"Change Language": "เปลี่ยนภาษา",
|
"Change Language": "เปลี่ยนภาษา",
|
||||||
"Date is too early": "วันที่เร็วเกินไป",
|
"Date is too early": "วันที่เร็วเกินไป",
|
||||||
|
"User Canceled": "User Canceled",
|
||||||
"Welcome to ezBookkeeping": "ยินดีต้อนรับสู่ ezBookkeeping",
|
"Welcome to ezBookkeeping": "ยินดีต้อนรับสู่ ezBookkeeping",
|
||||||
"Please log in with your ezBookkeeping account": "กรุณาเข้าสู่ระบบด้วยบัญชี ezBookkeeping ของคุณ",
|
"Please log in with your ezBookkeeping account": "กรุณาเข้าสู่ระบบด้วยบัญชี ezBookkeeping ของคุณ",
|
||||||
"Unlock Application": "ปลดล็อกแอปพลิเคชัน",
|
"Unlock Application": "ปลดล็อกแอปพลิเคชัน",
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "Очистити",
|
"Clear": "Очистити",
|
||||||
"Generate": "Generate",
|
"Generate": "Generate",
|
||||||
"Recognize": "Recognize",
|
"Recognize": "Recognize",
|
||||||
|
"Recognizing...": "Recognizing...",
|
||||||
|
"Cancel Recognition": "Cancel Recognition",
|
||||||
"None": "Немає",
|
"None": "Немає",
|
||||||
"Unspecified": "Не вказано",
|
"Unspecified": "Не вказано",
|
||||||
"Not set": "Не встановлено",
|
"Not set": "Не встановлено",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "Зберегти порядок відображення",
|
"Save Display Order": "Зберегти порядок відображення",
|
||||||
"Change Language": "Змінити мову",
|
"Change Language": "Змінити мову",
|
||||||
"Date is too early": "Дата занадто рання",
|
"Date is too early": "Дата занадто рання",
|
||||||
|
"User Canceled": "User Canceled",
|
||||||
"Welcome to ezBookkeeping": "Ласкаво просимо до ezBookkeeping",
|
"Welcome to ezBookkeeping": "Ласкаво просимо до ezBookkeeping",
|
||||||
"Please log in with your ezBookkeeping account": "Будь ласка, увійдіть до свого облікового запису ezBookkeeping",
|
"Please log in with your ezBookkeeping account": "Будь ласка, увійдіть до свого облікового запису ezBookkeeping",
|
||||||
"Unlock Application": "Розблокувати застосунок",
|
"Unlock Application": "Розблокувати застосунок",
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "Xóa",
|
"Clear": "Xóa",
|
||||||
"Generate": "Generate",
|
"Generate": "Generate",
|
||||||
"Recognize": "Recognize",
|
"Recognize": "Recognize",
|
||||||
|
"Recognizing...": "Recognizing...",
|
||||||
|
"Cancel Recognition": "Cancel Recognition",
|
||||||
"None": "Không có",
|
"None": "Không có",
|
||||||
"Unspecified": "Không xác định",
|
"Unspecified": "Không xác định",
|
||||||
"Not set": "Not set",
|
"Not set": "Not set",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "Lưu thứ tự hiển thị",
|
"Save Display Order": "Lưu thứ tự hiển thị",
|
||||||
"Change Language": "Thay đổi ngôn ngữ",
|
"Change Language": "Thay đổi ngôn ngữ",
|
||||||
"Date is too early": "Ngày quá sớm",
|
"Date is too early": "Ngày quá sớm",
|
||||||
|
"User Canceled": "User Canceled",
|
||||||
"Welcome to ezBookkeeping": "Chào mừng đến với ezBookkeeping",
|
"Welcome to ezBookkeeping": "Chào mừng đến với ezBookkeeping",
|
||||||
"Please log in with your ezBookkeeping account": "Vui lòng đăng nhập bằng tài khoản ezBookkeeping của bạn",
|
"Please log in with your ezBookkeeping account": "Vui lòng đăng nhập bằng tài khoản ezBookkeeping của bạn",
|
||||||
"Unlock Application": "Mở khóa ứng dụng",
|
"Unlock Application": "Mở khóa ứng dụng",
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "清除",
|
"Clear": "清除",
|
||||||
"Generate": "生成",
|
"Generate": "生成",
|
||||||
"Recognize": "识别",
|
"Recognize": "识别",
|
||||||
|
"Recognizing...": "正在识别...",
|
||||||
|
"Cancel Recognition": "取消识别",
|
||||||
"None": "无",
|
"None": "无",
|
||||||
"Unspecified": "未指定",
|
"Unspecified": "未指定",
|
||||||
"Not set": "未设置",
|
"Not set": "未设置",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "保存显示顺序",
|
"Save Display Order": "保存显示顺序",
|
||||||
"Change Language": "修改语言",
|
"Change Language": "修改语言",
|
||||||
"Date is too early": "日期过早",
|
"Date is too early": "日期过早",
|
||||||
|
"User Canceled": "用户已取消",
|
||||||
"Welcome to ezBookkeeping": "欢迎使用 ezBookkeeping",
|
"Welcome to ezBookkeeping": "欢迎使用 ezBookkeeping",
|
||||||
"Please log in with your ezBookkeeping account": "请使用您的 ezBookkeeping 账号登录",
|
"Please log in with your ezBookkeeping account": "请使用您的 ezBookkeeping 账号登录",
|
||||||
"Unlock Application": "解锁应用",
|
"Unlock Application": "解锁应用",
|
||||||
|
|||||||
@@ -1398,6 +1398,8 @@
|
|||||||
"Clear": "清除",
|
"Clear": "清除",
|
||||||
"Generate": "產生",
|
"Generate": "產生",
|
||||||
"Recognize": "識別",
|
"Recognize": "識別",
|
||||||
|
"Recognizing...": "正在識別...",
|
||||||
|
"Cancel Recognition": "取消識別",
|
||||||
"None": "無",
|
"None": "無",
|
||||||
"Unspecified": "未指定",
|
"Unspecified": "未指定",
|
||||||
"Not set": "未設置",
|
"Not set": "未設置",
|
||||||
@@ -1522,6 +1524,7 @@
|
|||||||
"Save Display Order": "儲存顯示順序",
|
"Save Display Order": "儲存顯示順序",
|
||||||
"Change Language": "變更語言",
|
"Change Language": "變更語言",
|
||||||
"Date is too early": "日期過早",
|
"Date is too early": "日期過早",
|
||||||
|
"User Canceled": "使用者已取消",
|
||||||
"Welcome to ezBookkeeping": "歡迎使用 ezBookkeeping",
|
"Welcome to ezBookkeeping": "歡迎使用 ezBookkeeping",
|
||||||
"Please log in with your ezBookkeeping account": "請使用您的 ezBookkeeping 帳號登入",
|
"Please log in with your ezBookkeeping account": "請使用您的 ezBookkeeping 帳號登入",
|
||||||
"Unlock Application": "解鎖應用程式",
|
"Unlock Application": "解鎖應用程式",
|
||||||
|
|||||||
@@ -1160,9 +1160,9 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function recognizeReceiptImage({ imageFile }: { imageFile: File }): Promise<RecognizedReceiptImageResponse> {
|
function recognizeReceiptImage({ imageFile, cancelableUuid }: { imageFile: File, cancelableUuid?: string }): Promise<RecognizedReceiptImageResponse> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
services.recognizeReceiptImage({ imageFile }).then(response => {
|
services.recognizeReceiptImage({ imageFile, cancelableUuid }).then(response => {
|
||||||
const data = response.data;
|
const data = response.data;
|
||||||
|
|
||||||
if (!data || !data.success || !data.result) {
|
if (!data || !data.success || !data.result) {
|
||||||
@@ -1172,6 +1172,10 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
|
|
||||||
resolve(data.result);
|
resolve(data.result);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
if (error.canceled) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
logger.error('failed to recognize image', error);
|
logger.error('failed to recognize image', error);
|
||||||
|
|
||||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
@@ -1185,6 +1189,10 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cancelRecognizeReceiptImage(cancelableUuid: string): void {
|
||||||
|
services.cancelRequest(cancelableUuid);
|
||||||
|
}
|
||||||
|
|
||||||
function parseImportDsvFile({ fileType, fileEncoding, importFile }: { fileType: string, fileEncoding?: string, importFile: File }): Promise<string[][]> {
|
function parseImportDsvFile({ fileType, fileEncoding, importFile }: { fileType: string, fileEncoding?: string, importFile: File }): Promise<string[][]> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
services.parseImportDsvFile({ fileType, fileEncoding, importFile }).then(response => {
|
services.parseImportDsvFile({ fileType, fileEncoding, importFile }).then(response => {
|
||||||
@@ -1399,6 +1407,7 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
saveTransaction,
|
saveTransaction,
|
||||||
deleteTransaction,
|
deleteTransaction,
|
||||||
recognizeReceiptImage,
|
recognizeReceiptImage,
|
||||||
|
cancelRecognizeReceiptImage,
|
||||||
parseImportDsvFile,
|
parseImportDsvFile,
|
||||||
parseImportTransaction,
|
parseImportTransaction,
|
||||||
importTransactions,
|
importTransactions,
|
||||||
|
|||||||
@@ -33,8 +33,10 @@
|
|||||||
{{ tt('Recognize') }}
|
{{ tt('Recognize') }}
|
||||||
<v-progress-circular indeterminate size="22" class="ms-2" v-if="recognizing"></v-progress-circular>
|
<v-progress-circular indeterminate size="22" class="ms-2" v-if="recognizing"></v-progress-circular>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
<v-btn color="secondary" variant="tonal" :disabled="loading"
|
||||||
|
@click="cancelRecognize" v-if="recognizing && cancelRecognizingUuid">{{ tt('Cancel Recognition') }}</v-btn>
|
||||||
<v-btn color="secondary" variant="tonal" :disabled="loading || recognizing"
|
<v-btn color="secondary" variant="tonal" :disabled="loading || recognizing"
|
||||||
@click="cancel">{{ tt('Cancel') }}</v-btn>
|
@click="cancel" v-if="!recognizing || !cancelRecognizingUuid">{{ tt('Cancel') }}</v-btn>
|
||||||
</div>
|
</div>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
@@ -58,6 +60,7 @@ import { SUPPORTED_IMAGE_EXTENSIONS } from '@/consts/file.ts';
|
|||||||
|
|
||||||
import type { RecognizedReceiptImageResponse } from '@/models/large_language_model.ts';
|
import type { RecognizedReceiptImageResponse } from '@/models/large_language_model.ts';
|
||||||
|
|
||||||
|
import { generateRandomUUID } from '@/lib/misc.ts';
|
||||||
import { compressJpgImage } from '@/lib/ui/common.ts';
|
import { compressJpgImage } from '@/lib/ui/common.ts';
|
||||||
import logger from '@/lib/logger.ts';
|
import logger from '@/lib/logger.ts';
|
||||||
|
|
||||||
@@ -76,6 +79,7 @@ let rejectFunc: ((reason?: unknown) => void) | null = null;
|
|||||||
const showState = ref<boolean>(false);
|
const showState = ref<boolean>(false);
|
||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
const recognizing = ref<boolean>(false);
|
const recognizing = ref<boolean>(false);
|
||||||
|
const cancelRecognizingUuid = ref<string | undefined>(undefined);
|
||||||
const imageFile = ref<File | null>(null);
|
const imageFile = ref<File | null>(null);
|
||||||
const imageSrc = ref<string | undefined>(undefined);
|
const imageSrc = ref<string | undefined>(undefined);
|
||||||
const isDragOver = ref<boolean>(false);
|
const isDragOver = ref<boolean>(false);
|
||||||
@@ -96,6 +100,7 @@ function open(): Promise<RecognizedReceiptImageResponse> {
|
|||||||
showState.value = true;
|
showState.value = true;
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
recognizing.value = false;
|
recognizing.value = false;
|
||||||
|
cancelRecognizingUuid.value = undefined;
|
||||||
imageFile.value = null;
|
imageFile.value = null;
|
||||||
imageSrc.value = undefined;
|
imageSrc.value = undefined;
|
||||||
|
|
||||||
@@ -136,16 +141,24 @@ function recognize(): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancelRecognizingUuid.value = generateRandomUUID();
|
||||||
recognizing.value = true;
|
recognizing.value = true;
|
||||||
|
|
||||||
transactionsStore.recognizeReceiptImage({
|
transactionsStore.recognizeReceiptImage({
|
||||||
imageFile: imageFile.value
|
imageFile: imageFile.value,
|
||||||
|
cancelableUuid: cancelRecognizingUuid.value
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
resolveFunc?.(response);
|
resolveFunc?.(response);
|
||||||
showState.value = false;
|
showState.value = false;
|
||||||
recognizing.value = false;
|
recognizing.value = false;
|
||||||
|
cancelRecognizingUuid.value = undefined;
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
if (error.canceled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
recognizing.value = false;
|
recognizing.value = false;
|
||||||
|
cancelRecognizingUuid.value = undefined;
|
||||||
|
|
||||||
if (!error.processed) {
|
if (!error.processed) {
|
||||||
snackbar.value?.showError(error);
|
snackbar.value?.showError(error);
|
||||||
@@ -153,11 +166,24 @@ function recognize(): void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cancelRecognize(): void {
|
||||||
|
if (!cancelRecognizingUuid.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionsStore.cancelRecognizeReceiptImage(cancelRecognizingUuid.value);
|
||||||
|
recognizing.value = false;
|
||||||
|
cancelRecognizingUuid.value = undefined;
|
||||||
|
|
||||||
|
snackbar.value?.showMessage('User Canceled');
|
||||||
|
}
|
||||||
|
|
||||||
function cancel(): void {
|
function cancel(): void {
|
||||||
rejectFunc?.();
|
rejectFunc?.();
|
||||||
showState.value = false;
|
showState.value = false;
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
recognizing.value = false;
|
recognizing.value = false;
|
||||||
|
cancelRecognizingUuid.value = undefined;
|
||||||
imageFile.value = null;
|
imageFile.value = null;
|
||||||
imageSrc.value = undefined;
|
imageSrc.value = undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user