feat: menu request list (#75)
* feat: i18n * feat: request list * refactor: hide stat transition on app.scss * feat: request list i18n * feat: request list => constants and main page * feat: add store * feat: add fetch data * feat: add utilities fn * feat: add store function / types * refactor: request list type * refactor: request list constants * refactor: quotation card => add customData and badge color props * feat: avatar group components * feat: request list group * refactor: request list => remove tab, add table data * feat: send search query * feat: add parameter * refactor: remove unused function * fix: rename component lits to list * feat: show stats from api * chore: cleanup * refactor: make it type safe * refactor: accept rotate flow id as parameter * feat: use page size component * feat: add component, data display & expansion product * feat: i18n * refactor: constants and request list table * refactor: type code, createdAt, updatedAt * refactor: utils function changThemeMode * feat: request list => view page * refactor: use type instead of infer from value * fix: function getEmployeeName att type * refactor: fetch work list * refactor: loop work list * feat: add i18n duty * feat: add form issue component * feat: add form issue section * fix: store error * refactor: edit by value * refactor: accept basic info from outside instead * feat: add status filter support on fetch * refactor: remove delete button * refactor: wording * feat/fix: request list i18n & constant * feat: document type * feat/refactor: request list => document expansion * refactor: doc expansion use FormGroupHead * refactor: fetch data based on id from route param * refactor: text area disable * feat: properties expansion display (mocking) * refactor: add document at product relation * refactor: edit get value product * feat: get workflow step to show on top * refactor: add type * refactor: add get attachment * refactor: add view attachment * refactor: edit file name * refactor: define props get hide icon * refactor: edit align row * refactor: by value table document * refactor: by value row table * feat: add independent ocr dialog * chore: clean up * refactor: accept more props and small adjustment * fix: error withDefault call * feat: accept default metadata when open * fix: typo * feat: add override hook when finish ocr * feat: reset state on open * feat: detect reader result is actually string * fix: variable name conflict * feat: properties to input component * feat: properties input in properties expansion * feat: properties expansion data (temporary) * refactor: add i18n status work * refactor: edit type work status and add step status * refactor: add edit status work * refactor: edit step work * refactor: properties data type * refactor: filter selected product & specific properties * feat: add emit event * refactor: change variable name for better understanding * refactor: hide step that no properties * refactor: work status type to validate * feat: work status color * refactor: key for filename * refactor: close expansion when change step * refactor: responsive meta data * refactor: product expansion responsive * fix: dark mode step text color * fix: document expansion table no data label * refactor: main page body bordered and overflow hidden * refactor: use utils function instead * refactor: add process * refactor: by value name * refactor: add upload file * refactor: upload file * refactor: by value * fix: option worker type * refactor: fetchRequestAttachment after edit * fix: metadata display * refactor: add class full-height * refactor: edit type * refactor: fetch file * refactor: by value visa * refactor: request list attributes type * fix: properties to input props (placeholder, readonly, disable) * feat: request list properties function * fix: error when no workflow * docs: update comment to fix indent * refactor: step type (attributes) * refactor: add attributes payload on editStatusRequestWork function * feat/refactor: functional form expansion/filter worklist * refactor: set attributes properties after submit * refactor: add request work ready status * feat: request list => form employee component * feat/refactor: form expansion select user/layout * fix: properties readonly --------- Co-authored-by: puriphatt <puriphat@frappet.com> Co-authored-by: Thanaphon Frappet <thanaphon@frappet.com>
This commit is contained in:
parent
9105dcf7fe
commit
972f6ba13e
36 changed files with 3653 additions and 57 deletions
140
src/components/upload-file/OcrDialog.vue
Normal file
140
src/components/upload-file/OcrDialog.vue
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
<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;
|
||||
};
|
||||
|
||||
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
|
||||
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>
|
||||
|
|
@ -105,7 +105,7 @@ async function change(e: Event) {
|
|||
const _file = _element?.files?.[0];
|
||||
|
||||
if (_file) {
|
||||
const newFileName = `${selectedMenu.value?.group}-${dateFormat(new Date().toISOString())}`;
|
||||
const newFileName = `${selectedMenu.value?.group}-${dateFormat(new Date().toISOString())}-${_file.name}`;
|
||||
const renamedFile = new File([_file], newFileName, { type: _file.type });
|
||||
|
||||
if (!obj.value[currentIndex.value] && selectedMenu.value) {
|
||||
|
|
@ -155,14 +155,12 @@ async function change(e: Event) {
|
|||
}
|
||||
|
||||
if (resOcr.group === 'passport') {
|
||||
const fullName = map['full_name'].split(' ');
|
||||
|
||||
obj.value[currentIndex.value]._meta = {
|
||||
type: map['doc_type'],
|
||||
number: map['doc_number'],
|
||||
gender: map['sex'],
|
||||
firstName: fullName[0],
|
||||
lastName: fullName[1],
|
||||
firstName: map['first_name'],
|
||||
lastName: map['last_name'],
|
||||
issueDate: map['issue_date'],
|
||||
expireDate: map['expire_date'],
|
||||
issuePlace: map['nationality'],
|
||||
|
|
@ -195,7 +193,7 @@ async function fileList() {
|
|||
.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
name: item.name?.split('-')[1],
|
||||
name: `${item.name?.split('-')[1]}-${item.name?.split('-')[2] || ''}`,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue