jws-frontend/src/components/upload-file/OcrDialog.vue
2024-12-24 13:19:18 +07:00

142 lines
3.2 KiB
Vue

<script setup lang="ts">
import { ref } from 'vue';
import ShowAttachment from 'components/ShowAttachent.vue';
import DialogForm from 'components/DialogForm.vue';
const isOpen = ref(false);
const isEdit = ref(false);
const isRunning = ref(false);
const file = ref<File>();
const url = ref<string>();
const metadata = ref<Data>();
const splitRatio = ref(50);
type Data = Record<string, any>;
type Props = {
title?: string;
readonly?: boolean;
autoSave?: boolean;
data?: Data;
hideBtn?: boolean;
};
type HandleProps = {
ocr?: (file: File) => Promise<false | Data>;
save?: (file: File, meta?: Data) => void | Promise<boolean>;
override?: (before: Data, after: Data) => Data;
};
defineEmits<{
(e: 'submit', file: File, meta?: Data): void;
}>();
defineExpose({ isRunning });
const props = withDefaults(defineProps<Props & HandleProps>(), { title: '' });
const input = (() => {
const _element = document.createElement('input');
_element.type = 'file';
_element.accept = 'image/jpeg,image/png';
_element.addEventListener('change', change);
return _element;
})();
async function change(e: Event) {
const _element = e.target as HTMLInputElement | null;
const _file = _element?.files?.[0];
if (!_file) return;
file.value = _file;
url.value = await new Promise<string>((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(_file);
reader.onload = () => {
if (typeof reader.result === 'string') {
return resolve(reader.result);
}
return reject();
};
});
if (!props.ocr) return;
if (props.data) metadata.value = structuredClone(props.data);
isOpen.value = true;
isRunning.value = true;
const ocrResult = await props.ocr(_file);
isRunning.value = false;
if (!ocrResult) return;
if (!props.override || !metadata.value) {
return (metadata.value = ocrResult);
}
if (Object.entries(metadata.value).some(([k, v]) => ocrResult[k] !== v)) {
return (metadata.value = props.override(metadata.value, ocrResult));
}
}
</script>
<template>
<slot
name="trigger"
:browse="
() => {
file = undefined;
url = undefined;
metadata = undefined;
input?.click();
}
"
/>
<DialogForm
v-if="file && url"
v-model:modal="isOpen"
style="position: absolute"
height="100vh"
weight="90%"
hide-close-event
hide-delete
hide-btn
edit
:title
:is-edit
:readonly
:edit-data="() => (isEdit = true)"
:undo="() => (isEdit = false)"
:close="() => (isOpen = false)"
:submit="
() => {
if (!file) return;
$emit('submit', file, metadata);
if (autoSave) save?.(file, metadata);
isOpen = false;
}
"
>
<q-splitter class="full-height" v-model="splitRatio">
<template #before>
<div class="full-height">
<slot name="viewer" :url :file>
<ShowAttachment :url :file />
</slot>
</div>
</template>
<template #after>
<div class="q-pa-md full-height">
<slot name="body" :metadata :is-running :is-edit />
</div>
</template>
</q-splitter>
</DialogForm>
</template>