2067 lines
73 KiB
Vue
2067 lines
73 KiB
Vue
<script setup lang="ts">
|
|
import { useI18n } from 'vue-i18n';
|
|
import { reactive, ref, watch, onMounted } from 'vue';
|
|
import { baseUrl, notify, setPrefixName } from 'src/stores/utils';
|
|
|
|
// NOTE: Import stores
|
|
import { dialog } from 'stores/utils';
|
|
import useOptionStore from 'src/stores/options';
|
|
import useEmployeeStore from 'src/stores/employee';
|
|
import { useEmployeeForm } from 'src/pages/03_customer-management/form';
|
|
import { waitAll } from 'src/stores/utils';
|
|
import { calculateAge, dateFormat, dateFormatJS } from 'src/utils/datetime';
|
|
import { dialogCheckData } from 'stores/utils';
|
|
import { useQuotationForm } from 'pages/05_quotation/form';
|
|
|
|
// NOTE Import Types
|
|
import { Employee } from 'src/stores/employee/types';
|
|
import { EmployeeWorker } from 'src/stores/quotations/types';
|
|
import { runOcr, parseResultMRZ } from 'src/utils/ocr';
|
|
import useOcrStore from 'stores/ocr';
|
|
|
|
// NOTE: Import Components
|
|
import {
|
|
AddButton,
|
|
SaveButton,
|
|
EditButton,
|
|
UndoButton,
|
|
DeleteButton,
|
|
MainButton,
|
|
CancelButton,
|
|
} from 'components/button';
|
|
import DialogContainer from 'components/dialog/DialogContainer.vue';
|
|
import DialogHeader from 'components/dialog/DialogHeader.vue';
|
|
import ImportWorker from './ImportWorker.vue';
|
|
import PersonCard from 'src/components/shared/PersonCard.vue';
|
|
import NewPersonCard from 'src/components/shared/NewPersonCard.vue';
|
|
import { Lang } from 'src/utils/ui';
|
|
import NoData from 'src/components/NoData.vue';
|
|
import FormEmployeePassport from 'components/03_customer-management/FormEmployeePassport.vue';
|
|
import FormEmployeeVisa from 'components/03_customer-management/FormEmployeeVisa.vue';
|
|
import FormReferDocument from 'src/components/05_quotation/FormReferDocument.vue';
|
|
import { UploadFileGroup, NoticeJobEmployment } from 'components/upload-file';
|
|
import FormPerson from 'components/02_personnel-management/FormPerson.vue';
|
|
import ProfileBanner from 'components/ProfileBanner.vue';
|
|
import DialogForm from 'components/DialogForm.vue';
|
|
import {
|
|
uploadFileListEmployee,
|
|
columnsAttachment,
|
|
} from 'src/pages/03_customer-management/constant';
|
|
import { storeToRefs } from 'pinia';
|
|
import ToggleView from 'src/components/shared/ToggleView.vue';
|
|
import TableWorker from 'src/components/shared/table/TableWorker.vue';
|
|
import { SideMenu } from 'src/components';
|
|
import BasicInformation from 'components/03_customer-management/employee/BasicInformation.vue';
|
|
import { AddressForm } from 'src/components/form';
|
|
import ExpirationDate from 'src/components/03_customer-management/ExpirationDate.vue';
|
|
import FormEmployeeHealthCheck from 'src/components/03_customer-management/FormEmployeeHealthCheck.vue';
|
|
import FormEmployeeWorkHistory from 'src/components/03_customer-management/FormEmployeeWorkHistory.vue';
|
|
import FormEmployeeOther from 'src/components/03_customer-management/FormEmployeeOther.vue';
|
|
|
|
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
|
|
|
|
const { t } = useI18n();
|
|
const employeeFormStore = useEmployeeForm();
|
|
const quotationForm = useQuotationForm();
|
|
const { locale } = useI18n();
|
|
const ocrStore = useOcrStore();
|
|
|
|
const test = false;
|
|
|
|
const viewMode = ref<boolean>(false);
|
|
|
|
const { employeeFormUndo, employeeConfirmUnsave, deleteEmployeeById } =
|
|
employeeFormStore;
|
|
|
|
const {
|
|
state: employeeFormState,
|
|
currentFromDataEmployee,
|
|
onCreateImageList,
|
|
statusEmployeeCreate,
|
|
refreshImageState,
|
|
} = storeToRefs(employeeFormStore);
|
|
|
|
const mrz = ref<Awaited<ReturnType<typeof parseResultMRZ>>>();
|
|
|
|
const formDataEmployee = ref<
|
|
EmployeeWorker & {
|
|
attachment?: {
|
|
name?: string;
|
|
group?: string;
|
|
url?: string;
|
|
file?: File;
|
|
_meta?: Record<string, any>;
|
|
}[];
|
|
}
|
|
>({
|
|
passportNo: '',
|
|
documentExpireDate: new Date(),
|
|
lastNameEN: '',
|
|
lastName: '',
|
|
middleNameEN: '',
|
|
middleName: '',
|
|
firstNameEN: '',
|
|
firstName: '',
|
|
namePrefix: '',
|
|
nationality: '',
|
|
gender: '',
|
|
dateOfBirth: new Date(),
|
|
attachment: [],
|
|
});
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
customerBranchId?: string;
|
|
disabledWorkerId?: string[];
|
|
preselectWorker?: Employee[];
|
|
}>(),
|
|
{},
|
|
);
|
|
|
|
const emits = defineEmits<{
|
|
(e: 'triggerCreateEmployee'): void;
|
|
(
|
|
e: 'success',
|
|
data: {
|
|
worker: Employee[];
|
|
newWorker: EmployeeWorker[];
|
|
},
|
|
): void;
|
|
}>();
|
|
|
|
const optionStore = useOptionStore();
|
|
const employeeStore = useEmployeeStore();
|
|
|
|
const open = defineModel<boolean>('open', { default: false });
|
|
const newWorkerList = defineModel<
|
|
(EmployeeWorker & {
|
|
attachment?: {
|
|
name?: string;
|
|
group?: string;
|
|
url?: string;
|
|
file?: File;
|
|
_meta?: Record<string, any>;
|
|
}[];
|
|
})[]
|
|
>('newWorkerList', { default: [] });
|
|
const workerSelected = ref<Employee[]>([]);
|
|
const workerList = ref<Employee[]>([]);
|
|
const importWorkerCriteria = ref<{
|
|
passport: string[] | null;
|
|
visa: string[] | null;
|
|
}>({
|
|
passport: [],
|
|
visa: [],
|
|
});
|
|
|
|
const state = reactive({
|
|
importWorker: false,
|
|
step: 1,
|
|
search: '',
|
|
});
|
|
|
|
async function removeNewWorker(index: number) {
|
|
deleteEmployeeById({
|
|
id: newWorkerList.value[index].id,
|
|
removeArray: () => newWorkerList.value.splice(index, 1),
|
|
});
|
|
}
|
|
|
|
function triggerCreateEmployee() {
|
|
employeeFormStore.resetFormDataEmployee(true);
|
|
setCurrentBranchId();
|
|
//setDefaultFormEmployee();
|
|
employeeFormState.value.dialogType = 'create';
|
|
employeeFormState.value.dialogModal = true;
|
|
employeeFormState.value.isEmployeeEdit = true;
|
|
}
|
|
|
|
function clean() {
|
|
workerList.value = [];
|
|
workerSelected.value = [];
|
|
open.value = false;
|
|
}
|
|
|
|
function selectedIndex(item: Employee) {
|
|
return workerSelected.value.findIndex((v) => v.id === item.id);
|
|
}
|
|
|
|
function toggleSelect(item: Employee) {
|
|
if (props.disabledWorkerId?.some((id) => id === item.id)) return;
|
|
|
|
const index = selectedIndex(item);
|
|
|
|
if (index === -1) {
|
|
workerSelected.value.push(item);
|
|
} else {
|
|
workerSelected.value.splice(index, 1);
|
|
}
|
|
}
|
|
|
|
function getEmployeeImageUrl(item: Employee) {
|
|
if (item.selectedImage) {
|
|
return `${API_BASE_URL}/employee/${item.id}/image/${item.selectedImage}`;
|
|
}
|
|
// NOTE: static image
|
|
return `/images/employee-avatar-${item.gender}.png`;
|
|
}
|
|
|
|
function init() {
|
|
if (props.preselectWorker) {
|
|
workerSelected.value = JSON.parse(JSON.stringify(props.preselectWorker));
|
|
}
|
|
getWorkerList();
|
|
}
|
|
|
|
async function getWorkerList() {
|
|
const ret = await employeeStore.fetchList({
|
|
pageSize: 100,
|
|
query: state.search,
|
|
passport: true,
|
|
customerBranchId: props.customerBranchId,
|
|
activeOnly: true,
|
|
});
|
|
|
|
if (!ret) return false;
|
|
|
|
workerList.value = ret.result;
|
|
}
|
|
|
|
async function getWorkerFromCriteria(
|
|
payload: typeof importWorkerCriteria.value,
|
|
) {
|
|
const ret = await employeeStore.fetchList({
|
|
payload: {
|
|
passport: payload.passport || undefined,
|
|
},
|
|
pageSize: 99999, // must include all possible worker
|
|
page: 1,
|
|
passport: true,
|
|
customerBranchId: props.customerBranchId,
|
|
activeOnly: true,
|
|
});
|
|
|
|
if (!ret) return false; // error, do not close dialog
|
|
|
|
workerSelected.value = ret.result.filter(
|
|
(lhs) => !props.disabledWorkerId?.find((rhs) => lhs.id === rhs),
|
|
);
|
|
|
|
if (workerSelected.value.length > 0) state.step = 2;
|
|
|
|
return true;
|
|
}
|
|
|
|
watch(() => state.search, getWorkerList);
|
|
|
|
watch(
|
|
() => formDataEmployee.value.dateOfBirth,
|
|
() => {
|
|
const age = calculateAge(formDataEmployee.value.dateOfBirth, 'year');
|
|
if (formDataEmployee.value.dateOfBirth && Number(age) < 15) {
|
|
dialog({
|
|
color: 'warning',
|
|
icon: 'mdi-alert',
|
|
title: t('dialog.title.youngWorker15'),
|
|
cancelText: t('general.edit'),
|
|
persistent: true,
|
|
message: t('dialog.message.youngWorker15'),
|
|
|
|
cancel: async () => {
|
|
formDataEmployee.value.dateOfBirth = null;
|
|
return;
|
|
},
|
|
});
|
|
}
|
|
|
|
if (
|
|
formDataEmployee.value.dateOfBirth &&
|
|
Number(age) > 15 &&
|
|
Number(age) <= 18
|
|
) {
|
|
dialog({
|
|
color: 'warning',
|
|
icon: 'mdi-alert',
|
|
title: t('dialog.title.youngWorker18'),
|
|
cancelText: t('general.cancel'),
|
|
actionText: t('general.confirm'),
|
|
persistent: true,
|
|
message: t('dialog.message.youngWorker18'),
|
|
action: () => {},
|
|
cancel: async () => {
|
|
formDataEmployee.value.dateOfBirth = null;
|
|
return;
|
|
},
|
|
});
|
|
}
|
|
},
|
|
);
|
|
|
|
function setCurrentBranchId() {
|
|
employeeFormState.value.currentBranchId = props.customerBranchId;
|
|
}
|
|
|
|
watch(
|
|
() => employeeFormState.value.currentCustomerBranch,
|
|
(e) => {
|
|
if (!e) return;
|
|
if (employeeFormState.value.formDataEmployeeSameAddr) {
|
|
currentFromDataEmployee.value.address = e.address;
|
|
currentFromDataEmployee.value.addressEN = e.addressEN;
|
|
currentFromDataEmployee.value.provinceId = e.provinceId;
|
|
currentFromDataEmployee.value.districtId = e.districtId;
|
|
currentFromDataEmployee.value.subDistrictId = e.subDistrictId;
|
|
}
|
|
currentFromDataEmployee.value.customerBranchId = e.id;
|
|
},
|
|
);
|
|
|
|
watch(
|
|
() => employeeFormState.value.formDataEmployeeSameAddr,
|
|
(isSame) => {
|
|
if (!employeeFormState.value.currentCustomerBranch) return;
|
|
if (isSame) {
|
|
currentFromDataEmployee.value.address =
|
|
employeeFormState.value.currentCustomerBranch.address;
|
|
currentFromDataEmployee.value.addressEN =
|
|
employeeFormState.value.currentCustomerBranch.addressEN;
|
|
currentFromDataEmployee.value.provinceId =
|
|
employeeFormState.value.currentCustomerBranch.provinceId;
|
|
currentFromDataEmployee.value.districtId =
|
|
employeeFormState.value.currentCustomerBranch.districtId;
|
|
currentFromDataEmployee.value.subDistrictId =
|
|
employeeFormState.value.currentCustomerBranch.subDistrictId;
|
|
}
|
|
currentFromDataEmployee.value.customerBranchId =
|
|
employeeFormState.value.currentCustomerBranch.id;
|
|
},
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<ImportWorker
|
|
v-model:open="state.importWorker"
|
|
v-model:data="importWorkerCriteria"
|
|
:import-worker="getWorkerFromCriteria"
|
|
/>
|
|
<DialogContainer v-model="open" @open="init" @close="clean">
|
|
<template #header>
|
|
<DialogHeader :title="$t('quotation.addWorker')">
|
|
<template #title-before>
|
|
<span class="q-mr-auto"></span>
|
|
</template>
|
|
<template #title-after>
|
|
<q-btn
|
|
class="q-ml-auto q-mr-xs"
|
|
flat
|
|
size="sm"
|
|
icon="mdi-dots-horizontal"
|
|
:id="`btn-kebab-action`"
|
|
@click.stop
|
|
:title="$t('general.additional')"
|
|
style="max-width: 20px"
|
|
>
|
|
<q-menu class="bordered" ref="refMenu">
|
|
<q-list>
|
|
<q-item
|
|
id="trigger-create-employee"
|
|
v-close-popup
|
|
dense
|
|
clickable
|
|
class="row items-center"
|
|
style="white-space: nowrap"
|
|
@click.stop="() => triggerCreateEmployee()"
|
|
>
|
|
<q-icon
|
|
size="xs"
|
|
class="q-mr-sm"
|
|
name="mdi-plus"
|
|
style="color: hsl(var(--info-bg))"
|
|
/>
|
|
<span>
|
|
{{ $t('quotation.addWorker') }}
|
|
</span>
|
|
</q-item>
|
|
|
|
<q-item
|
|
id="import-worker"
|
|
v-close-popup
|
|
dense
|
|
clickable
|
|
style="white-space: nowrap"
|
|
class="row items-center"
|
|
@click.stop="() => (state.importWorker = true)"
|
|
>
|
|
<q-icon
|
|
size="xs"
|
|
class="q-mr-sm"
|
|
name="mdi-import"
|
|
style="color: hsl(195 90% 50%)"
|
|
/>
|
|
<span>{{ $t('quotation.importWorker') }}</span>
|
|
</q-item>
|
|
</q-list>
|
|
</q-menu>
|
|
</q-btn>
|
|
</template>
|
|
</DialogHeader>
|
|
</template>
|
|
|
|
<div class="column full-height no-wrap surface-2">
|
|
<section class="row q-mb-md">
|
|
<header
|
|
class="row items-center q-py-sm q-px-md justify-end full-width surface-3 bordered-b no-wrap"
|
|
>
|
|
<ToggleView v-model="viewMode" class="q-mr-sm full-height" />
|
|
|
|
<q-input
|
|
for="input-search"
|
|
outlined
|
|
dense
|
|
:label="$t('general.search')"
|
|
:bg-color="$q.dark.isActive ? 'dark' : 'white'"
|
|
v-model="state.search"
|
|
debounce="500"
|
|
>
|
|
<template #prepend>
|
|
<q-icon name="mdi-magnify" />
|
|
</template>
|
|
</q-input>
|
|
</header>
|
|
</section>
|
|
<!-- wrapper -->
|
|
<div class="col q-gutter-y-md scroll q-px-md">
|
|
<q-expansion-item
|
|
v-if="newWorkerList.length !== 0"
|
|
for="item-up"
|
|
id="item-up"
|
|
dense
|
|
class="overflow-hidden"
|
|
switch-toggle-side
|
|
style="border-radius: var(--radius-2)"
|
|
expand-icon="mdi-chevron-down-circle"
|
|
default-opened
|
|
>
|
|
<template v-slot:header>
|
|
<section class="row items-center full-width">
|
|
<div class="row items-center q-pr-md q-py-sm">
|
|
<span
|
|
class="text-weight-medium q-mr-md"
|
|
style="font-size: 18px"
|
|
>
|
|
{{ $t('quotation.newCustomer') }}
|
|
</span>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<div class="full-width q-pt-md">
|
|
<section
|
|
:class="{ ['items-center']: workerList.length === 0 }"
|
|
class="row q-col-gutter-md"
|
|
>
|
|
<div
|
|
:key="emp.id"
|
|
v-for="(emp, index) in newWorkerList.map((data) => ({
|
|
...data,
|
|
}))"
|
|
class="col-2"
|
|
>
|
|
<button
|
|
class="selectable-item full-width selectable-item__selected"
|
|
>
|
|
<NewPersonCard
|
|
@cancel="(i) => removeNewWorker(i)"
|
|
no-action
|
|
class="full-width"
|
|
:prefix-id="'employee-' + index"
|
|
:data="{
|
|
name: ` ${emp.firstName ? `${emp.firstName} ${emp.lastName}` : `${emp.firstNameEN} ${emp.lastNameEN}`}`,
|
|
female: emp.gender === 'female',
|
|
male: emp.gender === 'male',
|
|
img: `/images/employee-avatar-${emp.gender}.png`,
|
|
index: index,
|
|
detail: [
|
|
{
|
|
icon: 'mdi-passport',
|
|
value: optionStore.mapOption(emp.nationality),
|
|
},
|
|
{
|
|
icon: 'mdi-clock-outline',
|
|
value: calculateAge(emp.dateOfBirth),
|
|
},
|
|
],
|
|
}"
|
|
/>
|
|
</button>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</q-expansion-item>
|
|
|
|
<q-expansion-item
|
|
for="item-up"
|
|
id="item-up"
|
|
dense
|
|
class="overflow-hidden"
|
|
switch-toggle-side
|
|
default-opened
|
|
style="border-radius: var(--radius-2)"
|
|
expand-icon="mdi-chevron-down-circle"
|
|
header-class=""
|
|
>
|
|
<template v-slot:header>
|
|
<section class="row items-center full-width">
|
|
<div class="row items-center q-pr-md q-py-sm">
|
|
<span
|
|
class="text-weight-medium q-mr-md"
|
|
style="font-size: 18px"
|
|
>
|
|
{{ $t('quotation.employee') }}
|
|
</span>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<div class="full-width q-pt-md">
|
|
<section
|
|
:class="{ ['items-center']: workerList.length === 0 }"
|
|
class="row scroll"
|
|
>
|
|
<div
|
|
style="display: inline-block; margin-inline: auto"
|
|
v-if="workerList.length === 0"
|
|
>
|
|
<NoData :not-found="!!state.search" />
|
|
</div>
|
|
<TableWorker
|
|
v-if="workerList.length !== 0"
|
|
v-model:selected="workerSelected"
|
|
:rows="
|
|
!!newWorkerList.length
|
|
? workerList.filter((v) =>
|
|
newWorkerList.some((x) => x.id !== v.id),
|
|
)
|
|
: workerList
|
|
"
|
|
:disabledWorkerId
|
|
:grid="viewMode"
|
|
>
|
|
<template #grid="{ item: emp, index }">
|
|
<div :key="emp.id" class="col-md-2 col-sm-6 col-12">
|
|
<button
|
|
:id="`select-worker-${emp.firstName}`"
|
|
class="selectable-item full-width"
|
|
:class="{
|
|
['selectable-item__selected']:
|
|
emp._selectedIndex !== -1,
|
|
['selectable-item__disabled']: disabledWorkerId?.some(
|
|
(id) => id === emp.id,
|
|
),
|
|
}"
|
|
@click="toggleSelect(emp)"
|
|
>
|
|
<span class="selectable-item__pos">
|
|
{{ (emp._selectedIndex || 0) + 1 }}
|
|
</span>
|
|
<PersonCard
|
|
no-action
|
|
class="full-width"
|
|
:prefix-id="'employee-' + index"
|
|
:data="{
|
|
name:
|
|
locale === Lang.English
|
|
? `${emp.firstNameEN} ${emp.lastNameEN}`
|
|
: `${emp.firstName} ${emp.lastName}`,
|
|
code: emp.employeePassport?.at(0)?.number || '-',
|
|
female: emp.gender === 'female',
|
|
male: emp.gender === 'male',
|
|
img: getEmployeeImageUrl(emp),
|
|
fallbackImg: `/images/employee-avatar-${emp.gender}.png`,
|
|
detail: [
|
|
{
|
|
icon: 'mdi-passport',
|
|
value: optionStore.mapOption(emp.nationality),
|
|
},
|
|
{
|
|
icon: 'mdi-clock-outline',
|
|
value: calculateAge(emp.dateOfBirth),
|
|
},
|
|
],
|
|
}"
|
|
/>
|
|
</button>
|
|
</div>
|
|
</template>
|
|
</TableWorker>
|
|
</section>
|
|
</div>
|
|
</q-expansion-item>
|
|
</div>
|
|
</div>
|
|
<template #footer>
|
|
<div class="q-gutter-x-xs q-ml-auto">
|
|
<CancelButton id="btn-cancel" outlined @click="clean" />
|
|
<MainButton
|
|
icon="mdi-check"
|
|
color="207 96% 32%"
|
|
solid
|
|
id="btn-success"
|
|
@click="
|
|
(emits('success', {
|
|
worker: workerSelected,
|
|
newWorker: newWorkerList,
|
|
}),
|
|
(open = false))
|
|
"
|
|
>
|
|
{{ $t('general.select', { msg: $t('quotation.employeeList') }) }}
|
|
</MainButton>
|
|
</div>
|
|
</template>
|
|
</DialogContainer>
|
|
|
|
<!-- NOTE: START - Employee Add Form -->
|
|
|
|
<DialogForm
|
|
hideFooter
|
|
ref="formDialogRef"
|
|
:title="$t('form.title.create', { name: $t('customer.employee') })"
|
|
v-model:modal="employeeFormState.dialogModal"
|
|
:undo="() => employeeFormUndo(false)"
|
|
:submit="
|
|
async () => {
|
|
if (employeeFormState.currentTab === 'personalInfo') {
|
|
const currentEmployeeId =
|
|
await employeeFormStore.submitPersonal(onCreateImageList);
|
|
quotationForm.injectNewEmployee({
|
|
data: { ...currentFromDataEmployee, id: currentEmployeeId },
|
|
});
|
|
employeeFormState.isEmployeeEdit = false;
|
|
employeeFormState.dialogType = 'info';
|
|
}
|
|
|
|
if (employeeFormState.currentTab === 'passport') {
|
|
await employeeFormStore.submitPassport();
|
|
}
|
|
|
|
if (employeeFormState.currentTab === 'visa') {
|
|
await employeeFormStore.submitVisa();
|
|
}
|
|
|
|
if (employeeFormState.currentTab === 'healthCheck') {
|
|
await employeeFormStore.submitHealthCheck();
|
|
}
|
|
|
|
if (employeeFormState.currentTab === 'workHistory') {
|
|
await employeeFormStore.submitWorkHistory();
|
|
}
|
|
|
|
if (employeeFormState.currentTab === 'other') {
|
|
await employeeFormStore.submitOther();
|
|
}
|
|
statusEmployeeCreate = true;
|
|
employeeFormState.isEmployeeEdit = false;
|
|
}
|
|
"
|
|
:show="
|
|
() => {
|
|
employeeFormStore.resetFormDataEmployee(true);
|
|
setCurrentBranchId();
|
|
}
|
|
"
|
|
:before-close="
|
|
() => {
|
|
if (employeeFormStore.isFormDataDifferent()) {
|
|
employeeConfirmUnsave();
|
|
return true;
|
|
}
|
|
employeeFormState.currentTab = 'personalInfo';
|
|
return false;
|
|
}
|
|
"
|
|
>
|
|
<div
|
|
:class="{
|
|
'q-mx-lg q-my-md': $q.screen.gt.sm,
|
|
'q-mx-md q-my-sm': !$q.screen.gt.sm,
|
|
}"
|
|
>
|
|
<ProfileBanner
|
|
active
|
|
useToggle
|
|
prefix="dialog"
|
|
color="white"
|
|
icon="mdi-account-plus-outline"
|
|
:bg-color="
|
|
employeeFormState.profileUrl
|
|
? 'white'
|
|
: 'linear-gradient(135deg, rgba(43,137,223,1) 0%, rgba(230,51,81,1) 100%)'
|
|
"
|
|
v-model:current-tab="employeeFormState.currentTab"
|
|
v-model:toggle-status="currentFromDataEmployee.status"
|
|
fallbackCover="/images/employee-banner.png"
|
|
:img="
|
|
`${baseUrl}/employee/${currentFromDataEmployee.id}/image/${currentFromDataEmployee.selectedImage}`.concat(
|
|
refreshImageState ? `?ts=${Date.now()}` : '',
|
|
) || null
|
|
"
|
|
:fallbackImg="`/images/employee-avatar-${currentFromDataEmployee.gender === 'male' ? 'male' : 'female'}.png`"
|
|
:tabs-list="
|
|
[
|
|
{
|
|
name: 'personalInfo',
|
|
label: $t('customerEmployee.form.group.personalInfo'),
|
|
},
|
|
{
|
|
name: 'passport',
|
|
label: $t('customerEmployee.fileType.passport'),
|
|
},
|
|
{
|
|
name: 'visa',
|
|
label: $t('customerEmployee.form.group.visa'),
|
|
},
|
|
|
|
{
|
|
name: 'healthCheck',
|
|
label: $t('customerEmployee.form.group.healthCheck'),
|
|
},
|
|
{
|
|
name: 'workHistory',
|
|
label: $t('customerEmployee.form.group.workHistory'),
|
|
},
|
|
{ name: 'other', label: $t('customerEmployee.form.group.other') },
|
|
].filter((v) => {
|
|
if (!employeeFormState.statusSavePersonal) {
|
|
return v.name === 'personalInfo';
|
|
}
|
|
|
|
return true;
|
|
})
|
|
"
|
|
:toggleTitle="$t('status.title')"
|
|
hideFade
|
|
:title="
|
|
currentFromDataEmployee
|
|
? setPrefixName(
|
|
{
|
|
namePrefix: currentFromDataEmployee.namePrefix,
|
|
firstName: currentFromDataEmployee.firstName,
|
|
lastName: currentFromDataEmployee.lastName,
|
|
firstNameEN: currentFromDataEmployee.firstNameEN,
|
|
lastNameEN: currentFromDataEmployee.lastNameEN,
|
|
},
|
|
{ locale },
|
|
)
|
|
: '-'
|
|
"
|
|
@view="
|
|
() => {
|
|
employeeFormState.imageDialog = true;
|
|
employeeFormState.isImageEdit = false;
|
|
}
|
|
"
|
|
@edit="
|
|
employeeFormState.imageDialog = employeeFormState.isImageEdit = true
|
|
"
|
|
@update:toggle-status="
|
|
() => {
|
|
currentFromDataEmployee.status =
|
|
currentFromDataEmployee.status === 'CREATED'
|
|
? 'INACTIVE'
|
|
: 'CREATED';
|
|
}
|
|
"
|
|
/>
|
|
</div>
|
|
|
|
<div
|
|
style="flex: 1; width: 100%; overflow-y: auto"
|
|
:class="{
|
|
'q-px-lg q-pb-lg': $q.screen.gt.sm,
|
|
'q-px-md q-pb-sm': !$q.screen.gt.sm,
|
|
}"
|
|
id="drawer-employee-form"
|
|
>
|
|
<div
|
|
class="col surface-1 full-height rounded bordered scroll row relative-position"
|
|
:key="String(!employeeFormState.isEmployeeEdit)"
|
|
>
|
|
<div
|
|
class="col"
|
|
style="height: 100%; max-height: 100; overflow-y: auto"
|
|
v-if="$q.screen.gt.sm"
|
|
>
|
|
<div class="q-py-md q-pl-md q-pr-sm">
|
|
<SideMenu
|
|
:menu="
|
|
[
|
|
{
|
|
name: $t('form.field.basicInformation'),
|
|
anchor: 'form-information',
|
|
tab: 'personalInfo',
|
|
},
|
|
{
|
|
name: $t('customerEmployee.form.group.personalInfo'),
|
|
anchor: 'form-personal',
|
|
tab: 'personalInfo',
|
|
},
|
|
{
|
|
name: $t('general.address'),
|
|
anchor: 'form-personal-address',
|
|
tab: 'personalInfo',
|
|
},
|
|
{
|
|
name: $t('general.uploadFile'),
|
|
anchor: 'form-info-file-upload',
|
|
tab: 'personalInfo',
|
|
},
|
|
|
|
{
|
|
name: $t('customerEmployee.form.group.passport'),
|
|
anchor: 'form-passport',
|
|
tab: 'passport',
|
|
useBtn:
|
|
currentFromDataEmployee.employeePassport?.filter(
|
|
(item) => {
|
|
if (item.id === undefined) {
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
).length === 0 &&
|
|
employeeFormState.currentIndexPassport === -1
|
|
? true
|
|
: false,
|
|
},
|
|
...(currentFromDataEmployee.employeePassport?.map((v, i) => ({
|
|
name: dateFormat(v.expireDate),
|
|
anchor: `form-employee-employeePassport-${i}`,
|
|
tab: 'passport',
|
|
sub: true,
|
|
})) || []),
|
|
|
|
{
|
|
name: $t('customerEmployee.form.group.visa'),
|
|
anchor: 'form-visa',
|
|
tab: 'visa',
|
|
useBtn:
|
|
currentFromDataEmployee.employeeVisa?.filter((item) => {
|
|
if (item.id === undefined) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}).length === 0 &&
|
|
employeeFormState.currentIndexVisa === -1
|
|
? true
|
|
: false,
|
|
},
|
|
|
|
...(currentFromDataEmployee.employeeVisa?.map((v, i) => ({
|
|
name: dateFormat(v.expireDate),
|
|
anchor: `form-employee-visa-${i}`,
|
|
tab: 'visa',
|
|
sub: true,
|
|
})) || []),
|
|
|
|
{
|
|
name: $t('customerEmployee.form.group.healthCheck'),
|
|
anchor: 'form-employee-checkup',
|
|
tab: 'healthCheck',
|
|
useBtn:
|
|
currentFromDataEmployee.employeeCheckup?.filter(
|
|
(item) => {
|
|
if (item.id === undefined) {
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
).length === 0 &&
|
|
employeeFormState.currentIndexCheckup === -1
|
|
? true
|
|
: false,
|
|
},
|
|
|
|
...(currentFromDataEmployee.employeeCheckup?.map((v, i) => ({
|
|
name: $t('general.times', { number: i + 1 }),
|
|
anchor: `form-employee-checkup-${i}`,
|
|
tab: 'healthCheck',
|
|
})) || []),
|
|
|
|
{
|
|
name: $t('customerEmployee.form.group.workHistory'),
|
|
anchor: 'form-employee-work-history',
|
|
tab: 'workHistory',
|
|
useBtn:
|
|
currentFromDataEmployee.employeeWork?.filter((item) => {
|
|
if (item.id === undefined) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}).length === 0 &&
|
|
employeeFormState.currentIndexWorkHistory === -1
|
|
? true
|
|
: false,
|
|
},
|
|
|
|
...(currentFromDataEmployee.employeeWork?.map((v, i) => ({
|
|
name: $t('general.times', { number: i + 1 }),
|
|
anchor: `form-employee-work-history-${i}`,
|
|
tab: 'workHistory',
|
|
})) || []),
|
|
|
|
{
|
|
name: $t('customerEmployee.form.group.family'),
|
|
anchor: 'form-employee-other',
|
|
tab: 'other',
|
|
},
|
|
].filter((v) => v.tab === employeeFormState.currentTab)
|
|
"
|
|
background="transparent"
|
|
:active="{
|
|
background: 'hsla(var(--blue-6-hsl) / .2)',
|
|
foreground: 'var(--blue-6)',
|
|
}"
|
|
scroll-element="#employee-form-content"
|
|
>
|
|
<template v-slot:btn-form-passport>
|
|
<q-btn
|
|
id="form-add-passport"
|
|
dense
|
|
flat
|
|
icon="mdi-plus"
|
|
size="sm"
|
|
rounded
|
|
padding="0px 0px"
|
|
style="color: var(--stone-9)"
|
|
@click.stop="employeeFormStore.addPassport()"
|
|
/>
|
|
</template>
|
|
|
|
<template v-slot:btn-form-visa>
|
|
<q-btn
|
|
id="form-add-visa"
|
|
dense
|
|
flat
|
|
icon="mdi-plus"
|
|
size="sm"
|
|
rounded
|
|
style="color: var(--stone-9)"
|
|
@click.stop="employeeFormStore.addVisa()"
|
|
/>
|
|
</template>
|
|
|
|
<template v-slot:btn-form-employee-checkup>
|
|
<q-btn
|
|
id="form-add-checkup"
|
|
dense
|
|
flat
|
|
icon="mdi-plus"
|
|
size="sm"
|
|
rounded
|
|
style="color: var(--stone-9)"
|
|
@click.stop="employeeFormStore.addCheckup()"
|
|
/>
|
|
</template>
|
|
|
|
<template v-slot:btn-form-employee-work-history>
|
|
<q-btn
|
|
id="form-add-work-history"
|
|
dense
|
|
flat
|
|
icon="mdi-plus"
|
|
size="sm"
|
|
rounded
|
|
style="color: var(--stone-9)"
|
|
@click.stop="employeeFormStore.addWorkHistory()"
|
|
/>
|
|
</template>
|
|
</SideMenu>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="col-12 col-md-10"
|
|
:class="{
|
|
'q-py-md q-pr-md ': $q.screen.gt.sm,
|
|
'q-pa-sm': !$q.screen.gt.sm,
|
|
}"
|
|
id="employee-form-content"
|
|
style="height: 100%; max-height: 100; overflow-y: auto"
|
|
>
|
|
<template v-if="employeeFormState.currentTab === 'personalInfo'">
|
|
<div
|
|
class="rounded row q-py-md q-px-md"
|
|
style="position: absolute; z-index: 999; top: 0; right: 0"
|
|
>
|
|
<div class="surface-1 row rounded">
|
|
<UndoButton
|
|
v-if="
|
|
employeeFormState.isEmployeeEdit &&
|
|
employeeFormState.dialogType !== 'create'
|
|
"
|
|
id="btn-info-basic-undo"
|
|
icon-only
|
|
@click="
|
|
() => {
|
|
employeeFormStore.resetFormDataEmployee();
|
|
employeeFormState.isEmployeeEdit = false;
|
|
employeeFormState.dialogType = 'info';
|
|
}
|
|
"
|
|
type="button"
|
|
/>
|
|
<SaveButton
|
|
v-if="employeeFormState.isEmployeeEdit"
|
|
id="btn-info-basic-save"
|
|
icon-only
|
|
type="submit"
|
|
/>
|
|
<EditButton
|
|
v-if="!employeeFormState.isEmployeeEdit"
|
|
id="btn-info-basic-edit"
|
|
icon-only
|
|
@click="
|
|
() => {
|
|
employeeFormState.isEmployeeEdit = true;
|
|
employeeFormState.dialogType = 'edit';
|
|
}
|
|
"
|
|
type="button"
|
|
/>
|
|
<DeleteButton
|
|
v-if="!employeeFormState.isEmployeeEdit"
|
|
id="btn-info-basic-delete"
|
|
icon-only
|
|
@click="() => removeNewWorker(newWorkerList.length - 1)"
|
|
type="button"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<BasicInformation
|
|
disable-customer-select
|
|
no-action
|
|
id="form-information"
|
|
prefix-id="form-employee"
|
|
:show-btn-save="employeeFormState.dialogType === 'create'"
|
|
employee
|
|
dense
|
|
outlined
|
|
separator
|
|
title="form.field.basicInformation"
|
|
:readonly="!employeeFormState.isEmployeeEdit"
|
|
v-model:customer-branch-id="employeeFormState.currentBranchId"
|
|
v-model:current-customer-branch="
|
|
employeeFormState.currentCustomerBranch
|
|
"
|
|
v-model:employee-id="employeeFormState.currentEmployeeCode"
|
|
v-model:nrc-no="currentFromDataEmployee.nrcNo"
|
|
v-model:code="currentFromDataEmployee.code"
|
|
class="q-mb-xl"
|
|
/>
|
|
<FormPerson
|
|
id="form-personal"
|
|
prefix-id="form-employee"
|
|
dense
|
|
outlined
|
|
employee
|
|
separator
|
|
title="customerEmployee.form.group.personalInfo"
|
|
:readonly="!employeeFormState.isEmployeeEdit"
|
|
v-model:open="employeeFormState.dialogModal"
|
|
v-model:prefix-name="currentFromDataEmployee.namePrefix"
|
|
v-model:first-name="currentFromDataEmployee.firstName"
|
|
v-model:last-name="currentFromDataEmployee.lastName"
|
|
v-model:first-name-en="currentFromDataEmployee.firstNameEN"
|
|
v-model:last-name-en="currentFromDataEmployee.lastNameEN"
|
|
v-model:mid-name="currentFromDataEmployee.middleName"
|
|
v-model:mid-name-en="currentFromDataEmployee.middleNameEN"
|
|
v-model:gender="currentFromDataEmployee.gender"
|
|
v-model:birth-date="currentFromDataEmployee.dateOfBirth"
|
|
v-model:nationality="currentFromDataEmployee.nationality"
|
|
class="q-mb-xl"
|
|
/>
|
|
<AddressForm
|
|
disabledRule
|
|
id="form-personal-address"
|
|
prefix-id="form-employee"
|
|
:readonly="!employeeFormState.isEmployeeEdit"
|
|
v-model:same-with-employer="
|
|
employeeFormState.formDataEmployeeSameAddr
|
|
"
|
|
v-model:address="currentFromDataEmployee.address"
|
|
v-model:address-en="currentFromDataEmployee.addressEN"
|
|
v-model:moo="currentFromDataEmployee.moo"
|
|
v-model:moo-en="currentFromDataEmployee.mooEN"
|
|
v-model:soi="currentFromDataEmployee.soi"
|
|
v-model:soi-en="currentFromDataEmployee.soiEN"
|
|
v-model:street="currentFromDataEmployee.street"
|
|
v-model:street-en="currentFromDataEmployee.streetEN"
|
|
v-model:province-id="currentFromDataEmployee.provinceId"
|
|
v-model:district-id="currentFromDataEmployee.districtId"
|
|
v-model:sub-district-id="currentFromDataEmployee.subDistrictId"
|
|
employee
|
|
dense
|
|
class="q-mb-xl"
|
|
/>
|
|
|
|
<div class="row q-mb-md" id="form-info-file-upload">
|
|
<div class="col-12 q-pb-sm text-weight-bold text-body1">
|
|
<q-icon
|
|
flat
|
|
size="xs"
|
|
class="q-pa-sm rounded q-mr-xs"
|
|
color="info"
|
|
name="mdi-upload"
|
|
style="background-color: var(--surface-3)"
|
|
/>
|
|
{{ $t(`general.uploadFile`) }}
|
|
</div>
|
|
<!-- สร้างลูกจ้าง -->
|
|
<UploadFileGroup
|
|
v-model:current-id="currentFromDataEmployee.id"
|
|
v-model="currentFromDataEmployee.file"
|
|
hide-action
|
|
:readonly="!employeeFormState.isEmployeeEdit"
|
|
:menu="uploadFileListEmployee"
|
|
:columns="columnsAttachment"
|
|
@submit="
|
|
async (group, allMeta) => {
|
|
if (allMeta === undefined) return;
|
|
|
|
if (group === 'passport') {
|
|
const fullName = allMeta['full_name'].split(' ');
|
|
let tempValue: {
|
|
oldData: { nameField: string; value: string }[];
|
|
newData: { nameField: string; value: string }[];
|
|
} = { oldData: [], newData: [] };
|
|
|
|
if (
|
|
currentFromDataEmployee.gender !== '' &&
|
|
currentFromDataEmployee.gender !== allMeta['sex']
|
|
) {
|
|
tempValue.oldData.push({
|
|
nameField: $t('form.gender'),
|
|
value: $t(
|
|
`general.${currentFromDataEmployee.gender}`,
|
|
),
|
|
});
|
|
tempValue.newData.push({
|
|
nameField: $t('form.gender'),
|
|
value: $t(`general.${allMeta['sex']}`),
|
|
});
|
|
}
|
|
|
|
if (currentFromDataEmployee.firstName !== '') {
|
|
tempValue.oldData.push({
|
|
nameField: $t('personnel.form.firstName'),
|
|
value: currentFromDataEmployee.firstName,
|
|
});
|
|
tempValue.newData.push({
|
|
nameField: $t('personnel.form.firstName'),
|
|
value: fullName[0],
|
|
});
|
|
}
|
|
|
|
if (currentFromDataEmployee.lastName !== '') {
|
|
tempValue.oldData.push({
|
|
nameField: $t('personnel.form.lastName'),
|
|
value: currentFromDataEmployee.lastName,
|
|
});
|
|
tempValue.newData.push({
|
|
nameField: $t('personnel.form.lastName'),
|
|
value: fullName[1],
|
|
});
|
|
}
|
|
|
|
if (currentFromDataEmployee.nationality !== '') {
|
|
tempValue.oldData.push({
|
|
nameField: $t('general.nationality'),
|
|
value: currentFromDataEmployee.nationality || '',
|
|
});
|
|
tempValue.newData.push({
|
|
nameField: $t('general.nationality'),
|
|
value: allMeta['nationality'],
|
|
});
|
|
}
|
|
|
|
dialogCheckData({
|
|
action: async () => {
|
|
currentFromDataEmployee.gender = allMeta['sex'];
|
|
currentFromDataEmployee.firstName = fullName[0];
|
|
currentFromDataEmployee.lastName = fullName[1];
|
|
currentFromDataEmployee.nationality =
|
|
allMeta['nationality'];
|
|
},
|
|
checkData: () => {
|
|
return tempValue;
|
|
},
|
|
cancel: () => {
|
|
if (!currentFromDataEmployee.gender) {
|
|
currentFromDataEmployee.gender = allMeta['gender'];
|
|
}
|
|
if (!currentFromDataEmployee.firstName) {
|
|
currentFromDataEmployee.firstName = fullName[0];
|
|
}
|
|
if (!currentFromDataEmployee.firstName) {
|
|
currentFromDataEmployee.firstName = fullName[0];
|
|
}
|
|
if (!currentFromDataEmployee.lastName) {
|
|
currentFromDataEmployee.lastName = fullName[1];
|
|
}
|
|
|
|
if (!currentFromDataEmployee.nationality) {
|
|
currentFromDataEmployee.nationality =
|
|
allMeta['nationality'];
|
|
}
|
|
},
|
|
});
|
|
}
|
|
}
|
|
"
|
|
:ocr="
|
|
async (group, file) => {
|
|
if (group === 'passport') {
|
|
mrz = await runOcr(file, parseResultMRZ);
|
|
|
|
if (mrz !== null) {
|
|
const mapMrz = Object.entries(mrz.result || {}).map(
|
|
([key, value]) => ({
|
|
name: key,
|
|
value: value,
|
|
}),
|
|
);
|
|
const tempValue = {
|
|
status: true,
|
|
group,
|
|
meta: mapMrz,
|
|
};
|
|
|
|
return tempValue;
|
|
}
|
|
}
|
|
if (group === 'visa') {
|
|
const res = await ocrStore.sendOcr({
|
|
file: file,
|
|
category: group,
|
|
});
|
|
|
|
if (res) {
|
|
const tempValue = {
|
|
status: true,
|
|
group,
|
|
meta: res.fields,
|
|
};
|
|
|
|
return tempValue;
|
|
}
|
|
}
|
|
|
|
return { status: true, group, meta: [] };
|
|
}
|
|
"
|
|
:auto-save="currentFromDataEmployee.id !== ''"
|
|
:download="
|
|
(obj) => {
|
|
if (obj.group !== 'attachment') {
|
|
employeeStore.getFile({
|
|
parentId: currentFromDataEmployee.id || '',
|
|
group: obj.group,
|
|
fileId: obj._meta.id,
|
|
download: true,
|
|
});
|
|
} else {
|
|
employeeStore.getAttachment({
|
|
parentId: currentFromDataEmployee.id || '',
|
|
name: obj._meta.name,
|
|
download: true,
|
|
});
|
|
}
|
|
}
|
|
"
|
|
:delete-item="
|
|
async (obj) => {
|
|
let status: boolean = false;
|
|
const res = await employeeStore.delMeta({
|
|
parentId: currentFromDataEmployee.id || '',
|
|
group: obj.group,
|
|
metaId: obj._meta.id,
|
|
});
|
|
|
|
if (res) {
|
|
status = true;
|
|
}
|
|
|
|
await employeeFormStore.assignFormDataEmployee(
|
|
currentFromDataEmployee.id,
|
|
);
|
|
|
|
return status;
|
|
}
|
|
"
|
|
:save="
|
|
async (
|
|
group: 'passport' | 'visa' | 'attachment',
|
|
_meta: any,
|
|
file: File | undefined,
|
|
) => {
|
|
let status: boolean = false;
|
|
if (group !== 'attachment') {
|
|
if (file !== undefined && currentFromDataEmployee.id) {
|
|
const res = await employeeStore.postMeta({
|
|
parentId: currentFromDataEmployee.id || '',
|
|
group,
|
|
meta: _meta,
|
|
file,
|
|
});
|
|
|
|
if (res) {
|
|
status = true;
|
|
}
|
|
} else {
|
|
const {
|
|
id,
|
|
employeeId,
|
|
createdAt,
|
|
updatedAt,
|
|
...payload
|
|
} = _meta;
|
|
|
|
const res = await employeeStore.putMeta({
|
|
parentId: currentFromDataEmployee.id || '',
|
|
group,
|
|
metaId: _meta.id,
|
|
meta: payload,
|
|
file,
|
|
});
|
|
if (res) {
|
|
status = true;
|
|
}
|
|
}
|
|
} else {
|
|
if (file !== undefined) {
|
|
await employeeStore.uploadAttachment(
|
|
currentFromDataEmployee.id || '',
|
|
file,
|
|
file.name,
|
|
);
|
|
status = true;
|
|
}
|
|
}
|
|
await employeeFormStore.assignFormDataEmployee(
|
|
currentFromDataEmployee.id,
|
|
);
|
|
return status;
|
|
}
|
|
"
|
|
:get-file-list="
|
|
async (group: 'passport' | 'visa' | 'attachment') => {
|
|
if (
|
|
!!currentFromDataEmployee.id &&
|
|
group !== 'attachment'
|
|
) {
|
|
const resMeta = await employeeStore.getMetaList({
|
|
parentId: currentFromDataEmployee.id,
|
|
group,
|
|
});
|
|
|
|
const tempValue = resMeta.map(async (i: any) => {
|
|
return {
|
|
_meta: { ...i },
|
|
name: `${group}-${dateFormat(i.expireDate)}` || '',
|
|
group: group,
|
|
url: await employeeStore.getFile({
|
|
parentId: currentFromDataEmployee.id || '',
|
|
group,
|
|
fileId: i.id,
|
|
}),
|
|
file: undefined,
|
|
};
|
|
});
|
|
|
|
return await waitAll(tempValue);
|
|
} else {
|
|
const res = await employeeStore.listAttachment({
|
|
parentId: currentFromDataEmployee.id || '',
|
|
});
|
|
|
|
const tempValue = (res as string[]).map(
|
|
async (i: any) => {
|
|
return {
|
|
_meta: { id: i, name: i },
|
|
name: i || '',
|
|
group: group,
|
|
url: await employeeStore.getAttachment({
|
|
parentId: currentFromDataEmployee.id || '',
|
|
name: i,
|
|
}),
|
|
file: undefined,
|
|
};
|
|
},
|
|
);
|
|
|
|
return await waitAll(tempValue);
|
|
}
|
|
}
|
|
"
|
|
>
|
|
<template #form="{ mode, meta, isEdit }">
|
|
<FormEmployeePassport
|
|
v-if="mode === 'passport' && meta"
|
|
prefix-id="drawer-info-employee"
|
|
id="form-passport"
|
|
dense
|
|
outlined
|
|
separator
|
|
ocr
|
|
:title="$t('customerEmployee.form.group.passport')"
|
|
:readonly="!isEdit"
|
|
v-model:birth-country="meta.birthCountry"
|
|
v-model:previous-passportRef="meta.previousPassportRef"
|
|
v-model:issue-place="meta.issuePlace"
|
|
v-model:issue-country="meta.issueCountry"
|
|
v-model:issue-date="meta.issueDate"
|
|
v-model:type="meta.type"
|
|
v-model:expire-date="meta.expireDate"
|
|
v-model:birth-date="meta.birthDate"
|
|
v-model:worker-status="meta.workerStatus"
|
|
v-model:nationality="meta.nationality"
|
|
v-model:gender="meta.gender"
|
|
v-model:last-name-en="meta.lastNameEN"
|
|
v-model:last-name="meta.lastName"
|
|
v-model:middle-name-en="meta.middleNameEN"
|
|
v-model:middle-name="meta.middleName"
|
|
v-model:first-name-en="meta.firstNameEN"
|
|
v-model:first-name="meta.firstName"
|
|
v-model:name-prefix="meta.namePrefix"
|
|
v-model:passport-number="meta.number"
|
|
/>
|
|
<FormEmployeeVisa
|
|
v-if="mode === 'visa' && meta"
|
|
prefix-id="drawer-info-employee"
|
|
id="form-visa"
|
|
ocr
|
|
dense
|
|
outlined
|
|
title="customerEmployee.form.group.visa"
|
|
:readonly="!isEdit"
|
|
v-model:arrival-at="meta.arrivalAt"
|
|
v-model:arrival-tm-no="meta.arrivalTMNo"
|
|
v-model:arrival-tm="meta.arrivalTM"
|
|
v-model:mrz="meta.mrz"
|
|
v-model:entry-count="meta.entryCount"
|
|
v-model:issue-place="meta.issuePlace"
|
|
v-model:issue-country="meta.issueCountry"
|
|
v-model:issueDate="meta.issueDate"
|
|
v-model:type="meta.type"
|
|
v-model:expire-date="meta.expireDate"
|
|
v-model:visa-issue-date="meta.issueDate"
|
|
v-model:visa-expiry-date="meta.expireDate"
|
|
v-model:remark="meta.remark"
|
|
v-model:worker-type="meta.workerType"
|
|
v-model:number="meta.number"
|
|
v-model:report-date="meta.reportDate"
|
|
/>
|
|
|
|
<NoticeJobEmployment v-if="mode === 'noticeJobEmployment'" />
|
|
</template>
|
|
</UploadFileGroup>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-if="employeeFormState.currentTab === 'passport'">
|
|
<div class="q-gutter-sm">
|
|
<div
|
|
class="col-12 q-pb-sm text-weight-bold text-body1 row items-center"
|
|
>
|
|
<q-icon
|
|
flat
|
|
size="xs"
|
|
class="q-pa-sm rounded q-mr-xs"
|
|
color="info"
|
|
name="mdi-passport"
|
|
style="background-color: var(--surface-3)"
|
|
/>
|
|
{{ $t('customerEmployee.form.group.passport') }}
|
|
<AddButton
|
|
v-if="$q.screen.lt.md"
|
|
id="btn-add-work"
|
|
icon-only
|
|
class="q-ml-sm"
|
|
:disabled="
|
|
currentFromDataEmployee.employeePassport?.filter((item) => {
|
|
if (item.id === undefined) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}).length === 0 &&
|
|
employeeFormState.currentIndexPassport === -1
|
|
? false
|
|
: true
|
|
"
|
|
@click.stop="employeeFormStore.addPassport()"
|
|
/>
|
|
</div>
|
|
|
|
<FormEmployeePassport
|
|
v-for="(
|
|
value, index
|
|
) in currentFromDataEmployee.employeePassport"
|
|
prefix-id="drawer-info-employee"
|
|
:key="index"
|
|
id="form-passport"
|
|
hide-title
|
|
dense
|
|
outlined
|
|
separator
|
|
:title="$t('customerEmployee.form.group.passport')"
|
|
:readonly="employeeFormState.currentIndexPassport !== index"
|
|
:full-name="employeeFormState.currentIndexPassport !== index"
|
|
v-model:birth-country="value.birthCountry"
|
|
v-model:previous-passportRef="value.previousPassportRef"
|
|
v-model:issue-place="value.issuePlace"
|
|
v-model:issue-country="value.issueCountry"
|
|
v-model:issue-date="value.issueDate"
|
|
v-model:type="value.type"
|
|
v-model:expire-date="value.expireDate"
|
|
v-model:birth-date="value.birthDate"
|
|
v-model:worker-status="value.workerStatus"
|
|
v-model:nationality="value.nationality"
|
|
v-model:gender="value.gender"
|
|
v-model:last-name-en="value.lastNameEN"
|
|
v-model:last-name="value.lastName"
|
|
v-model:middle-name-en="value.middleNameEN"
|
|
v-model:middle-name="value.middleName"
|
|
v-model:first-name-en="value.firstNameEN"
|
|
v-model:first-name="value.firstName"
|
|
v-model:name-prefix="value.namePrefix"
|
|
v-model:passport-number="value.number"
|
|
>
|
|
<template v-slot:expiryDate>
|
|
{{ $t('general.expirationDate') }} :
|
|
{{ dateFormat(value.expireDate) }}
|
|
<ExpirationDate
|
|
v-if="value.id !== undefined"
|
|
:expiration-date="value.expireDate"
|
|
/>
|
|
</template>
|
|
|
|
<template v-slot:button>
|
|
<div class="surface-1 row rounded" style="min-height: 35px">
|
|
<UndoButton
|
|
v-if="
|
|
employeeFormState.isEmployeeEdit &&
|
|
!(employeeFormState.currentIndexPassport === -1) &&
|
|
employeeFormState.currentIndexPassport === index
|
|
"
|
|
id="btn-info-basic-undo"
|
|
icon-only
|
|
@click="
|
|
() => {
|
|
employeeFormStore.resetFormDataEmployee();
|
|
employeeFormState.isEmployeeEdit = false;
|
|
employeeFormState.dialogType = 'info';
|
|
employeeFormState.currentIndexPassport = -1;
|
|
}
|
|
"
|
|
type="button"
|
|
/>
|
|
<SaveButton
|
|
v-if="
|
|
(employeeFormState.isEmployeeEdit ||
|
|
value.id === undefined) &&
|
|
!(employeeFormState.currentIndexPassport === -1) &&
|
|
employeeFormState.currentIndexPassport === index
|
|
"
|
|
id="btn-info-basic-save"
|
|
icon-only
|
|
@click="
|
|
() => {
|
|
employeeFormState.currentIndexPassport = index;
|
|
}
|
|
"
|
|
type="submit"
|
|
/>
|
|
|
|
<EditButton
|
|
v-if="
|
|
employeeFormState.currentIndexPassport === -1 ||
|
|
(!employeeFormState.isEmployeeEdit &&
|
|
value.id !== undefined &&
|
|
employeeFormState.currentIndexPassport === index)
|
|
"
|
|
id="btn-info-basic-edit"
|
|
icon-only
|
|
@click="
|
|
() => {
|
|
employeeFormState.currentIndexPassport = index;
|
|
employeeFormState.isEmployeeEdit = true;
|
|
employeeFormState.dialogType = 'edit';
|
|
}
|
|
"
|
|
type="button"
|
|
/>
|
|
<DeleteButton
|
|
v-if="
|
|
employeeFormState.currentIndexPassport === -1 ||
|
|
(!employeeFormState.isEmployeeEdit &&
|
|
value.id !== undefined &&
|
|
!(employeeFormState.currentIndexPassport === -1) &&
|
|
employeeFormState.currentIndexPassport === index)
|
|
"
|
|
id="btn-info-basic-delete"
|
|
icon-only
|
|
@click.stop="
|
|
() => {
|
|
employeeFormState.currentIndexPassport = index;
|
|
deleteEmployeeById({ type: 'passport', index });
|
|
}
|
|
"
|
|
type="button"
|
|
:disabled="
|
|
!(employeeFormState.currentIndexPassport === -1) &&
|
|
!(employeeFormState.currentIndexPassport === index)
|
|
"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</FormEmployeePassport>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-if="employeeFormState.currentTab === 'visa'">
|
|
<div class="q-gutter-sm">
|
|
<div
|
|
class="col-12 q-pb-sm text-weight-bold text-body1 row items-center"
|
|
>
|
|
<q-icon
|
|
flat
|
|
size="xs"
|
|
class="q-pa-sm rounded q-mr-xs"
|
|
color="info"
|
|
name="mdi-passport"
|
|
style="background-color: var(--surface-3)"
|
|
/>
|
|
{{ $t('customerEmployee.form.group.visa') }}
|
|
<AddButton
|
|
v-if="$q.screen.lt.md"
|
|
id="btn-add-work"
|
|
icon-only
|
|
class="q-ml-sm"
|
|
:disabled="
|
|
currentFromDataEmployee.employeeVisa?.filter((item) => {
|
|
if (item.id === undefined) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}).length === 0 && employeeFormState.currentIndexVisa === -1
|
|
? false
|
|
: true
|
|
"
|
|
@click.stop="employeeFormStore.addVisa()"
|
|
/>
|
|
</div>
|
|
|
|
<FormEmployeeVisa
|
|
v-for="(value, index) in currentFromDataEmployee.employeeVisa"
|
|
:key="index"
|
|
prefix-id="drawer-info-employee"
|
|
id="form-visa"
|
|
dense
|
|
outlined
|
|
title="customerEmployee.form.group.visa"
|
|
:readonly="employeeFormState.currentIndexVisa !== index"
|
|
hide-title
|
|
v-model:arrival-at="value.arrivalAt"
|
|
v-model:arrival-tm-no="value.arrivalTMNo"
|
|
v-model:arrival-tm="value.arrivalTM"
|
|
v-model:mrz="value.mrz"
|
|
v-model:entry-count="value.entryCount"
|
|
v-model:issue-place="value.issuePlace"
|
|
v-model:issue-country="value.issueCountry"
|
|
v-model:issueDate="value.issueDate"
|
|
v-model:type="value.type"
|
|
v-model:expire-date="value.expireDate"
|
|
v-model:visa-issue-date="value.issueDate"
|
|
v-model:visa-expiry-date="value.expireDate"
|
|
v-model:remark="value.remark"
|
|
v-model:worker-type="value.workerType"
|
|
v-model:number="value.number"
|
|
v-model:report-date="value.reportDate"
|
|
>
|
|
<template v-slot:expiryDate>
|
|
{{ $t('general.expirationDate') }} :
|
|
{{ dateFormat(value.expireDate) }}
|
|
<ExpirationDate
|
|
v-if="value.id !== undefined"
|
|
:expiration-date="value.expireDate"
|
|
/>
|
|
</template>
|
|
|
|
<template v-slot:button>
|
|
<div class="surface-1 row rounded" style="min-height: 35px">
|
|
<UndoButton
|
|
v-if="
|
|
employeeFormState.isEmployeeEdit &&
|
|
!(employeeFormState.currentIndexVisa === -1) &&
|
|
employeeFormState.currentIndexVisa === index
|
|
"
|
|
id="btn-info-basic-undo"
|
|
icon-only
|
|
@click="
|
|
() => {
|
|
employeeFormStore.resetFormDataEmployee();
|
|
employeeFormState.isEmployeeEdit = false;
|
|
employeeFormState.dialogType = 'info';
|
|
employeeFormState.currentIndexVisa = -1;
|
|
}
|
|
"
|
|
type="button"
|
|
/>
|
|
<SaveButton
|
|
v-if="
|
|
(employeeFormState.isEmployeeEdit ||
|
|
value.id === undefined) &&
|
|
!(employeeFormState.currentIndexVisa === -1) &&
|
|
employeeFormState.currentIndexVisa === index
|
|
"
|
|
id="btn-info-basic-save"
|
|
icon-only
|
|
@click="
|
|
() => {
|
|
employeeFormState.currentIndexVisa = index;
|
|
}
|
|
"
|
|
type="submit"
|
|
/>
|
|
|
|
<EditButton
|
|
v-if="
|
|
employeeFormState.currentIndexVisa === -1 ||
|
|
(!employeeFormState.isEmployeeEdit &&
|
|
value.id !== undefined &&
|
|
employeeFormState.currentIndexVisa === index)
|
|
"
|
|
id="btn-info-basic-edit"
|
|
icon-only
|
|
@click="
|
|
() => {
|
|
employeeFormState.currentIndexVisa = index;
|
|
employeeFormState.isEmployeeEdit = true;
|
|
employeeFormState.dialogType = 'edit';
|
|
}
|
|
"
|
|
type="button"
|
|
/>
|
|
<DeleteButton
|
|
v-if="
|
|
employeeFormState.currentIndexVisa === -1 ||
|
|
(!employeeFormState.isEmployeeEdit &&
|
|
value.id !== undefined &&
|
|
!(employeeFormState.currentIndexVisa === -1) &&
|
|
employeeFormState.currentIndexVisa === index)
|
|
"
|
|
id="btn-info-basic-delete"
|
|
icon-only
|
|
@click.stop="
|
|
() => {
|
|
employeeFormState.currentIndexVisa = index;
|
|
deleteEmployeeById({ type: 'visa', index });
|
|
}
|
|
"
|
|
type="button"
|
|
:disabled="
|
|
!(employeeFormState.currentIndexVisa === -1) &&
|
|
!(employeeFormState.currentIndexVisa === index)
|
|
"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</FormEmployeeVisa>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-if="employeeFormState.currentTab === 'healthCheck'">
|
|
<div class="q-gutter-sm">
|
|
<div class="q-pb-sm text-weight-bold text-body1 row items-center">
|
|
<q-icon
|
|
flat
|
|
size="xs"
|
|
class="q-pa-sm rounded q-mr-sm"
|
|
color="info"
|
|
name="mdi-hospital-box-outline"
|
|
style="background-color: var(--surface-3)"
|
|
/>
|
|
|
|
{{ $t(`customerEmployee.formHealthCheck.title`) }}
|
|
<AddButton
|
|
v-if="$q.screen.lt.md"
|
|
id="btn-add-work"
|
|
icon-only
|
|
class="q-ml-sm"
|
|
:disabled="
|
|
currentFromDataEmployee.employeeCheckup?.filter((item) => {
|
|
if (item.id === undefined) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}).length === 0 &&
|
|
employeeFormState.currentIndexCheckup === -1
|
|
? false
|
|
: true
|
|
"
|
|
@click.stop="employeeFormStore.addCheckup()"
|
|
/>
|
|
</div>
|
|
<FormEmployeeHealthCheck
|
|
v-if="employeeFormState.currentTab === 'healthCheck'"
|
|
id="form-checkup"
|
|
prefix-id="form-employee"
|
|
dense
|
|
outlined
|
|
v-model:current-index="employeeFormState.currentIndexCheckup"
|
|
v-model:employee-checkup="
|
|
currentFromDataEmployee.employeeCheckup
|
|
"
|
|
v-model:checkup-results-option="
|
|
optionStore.globalOption.checkupResults
|
|
"
|
|
v-model:checkup-type-option="
|
|
optionStore.globalOption.insurancePlace
|
|
"
|
|
v-model:medical-benefit-option="
|
|
optionStore.globalOption.typeInsurance
|
|
"
|
|
v-model:insurance-company-option="
|
|
optionStore.globalOption.insurancePlace
|
|
"
|
|
@delete="
|
|
(index) => {
|
|
employeeFormState.currentIndexCheckup = index;
|
|
deleteEmployeeById({ type: 'healthCheck', index });
|
|
}
|
|
"
|
|
@save="
|
|
(index) => {
|
|
employeeFormState.currentIndexCheckup = index;
|
|
notify('create', $t('general.success'));
|
|
}
|
|
"
|
|
@undo="
|
|
(index) => {
|
|
if (
|
|
currentFromDataEmployee.employeeCheckup?.[index]
|
|
.statusSave === false
|
|
) {
|
|
currentFromDataEmployee.employeeCheckup[
|
|
index
|
|
].statusSave = true;
|
|
}
|
|
}
|
|
"
|
|
@edit="
|
|
(index) => {
|
|
if (
|
|
currentFromDataEmployee.employeeCheckup?.[index]
|
|
.statusSave
|
|
) {
|
|
currentFromDataEmployee.employeeCheckup[
|
|
index
|
|
].statusSave = false;
|
|
}
|
|
}
|
|
"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-if="employeeFormState.currentTab === 'workHistory'">
|
|
<div class="q-gutter-sm">
|
|
<div
|
|
class="col-12 q-pb-sm text-weight-bold text-body1 row items-center"
|
|
>
|
|
<q-icon
|
|
flat
|
|
size="xs"
|
|
class="q-pa-sm rounded q-mr-xs"
|
|
color="info"
|
|
name="mdi-briefcase-outline"
|
|
style="background-color: var(--surface-3)"
|
|
/>
|
|
{{ $t(`customerEmployee.form.group.workHistory`) }}
|
|
<AddButton
|
|
v-if="$q.screen.lt.md"
|
|
id="btn-add-work"
|
|
icon-only
|
|
class="q-ml-sm"
|
|
:disabled="
|
|
currentFromDataEmployee.employeeWork?.filter((item) => {
|
|
if (item.id === undefined) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}).length === 0 &&
|
|
employeeFormState.currentIndexWorkHistory === -1
|
|
? false
|
|
: true
|
|
"
|
|
@click.stop="employeeFormStore.addWorkHistory()"
|
|
/>
|
|
</div>
|
|
<FormEmployeeWorkHistory
|
|
v-if="employeeFormState.currentTab === 'workHistory'"
|
|
id="form-work-history"
|
|
prefix-id="form-employee"
|
|
dense
|
|
outlined
|
|
v-model:current-index="
|
|
employeeFormState.currentIndexWorkHistory
|
|
"
|
|
v-model:employee-work="currentFromDataEmployee.employeeWork"
|
|
v-model:position-name-option="optionStore.globalOption.position"
|
|
v-model:job-type-option="optionStore.globalOption.businessType"
|
|
v-model:workplace-option="optionStore.globalOption.area"
|
|
@delete="
|
|
(index) => {
|
|
employeeFormState.currentIndexWorkHistory = index;
|
|
deleteEmployeeById({ type: 'work', index });
|
|
}
|
|
"
|
|
@save="
|
|
(index) => {
|
|
employeeFormState.currentIndexWorkHistory = index;
|
|
}
|
|
"
|
|
@undo="
|
|
(index) => {
|
|
if (
|
|
currentFromDataEmployee.employeeWork?.[index]
|
|
.statusSave === false
|
|
) {
|
|
currentFromDataEmployee.employeeWork[index].statusSave =
|
|
true;
|
|
}
|
|
}
|
|
"
|
|
@edit="
|
|
(index) => {
|
|
if (
|
|
currentFromDataEmployee.employeeWork?.[index].statusSave
|
|
) {
|
|
currentFromDataEmployee.employeeWork[index].statusSave =
|
|
false;
|
|
}
|
|
}
|
|
"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-if="employeeFormState.currentTab === 'other'">
|
|
<FormEmployeeOther
|
|
v-if="employeeFormState.currentTab === 'other'"
|
|
id="form-employee-other"
|
|
prefix-id="form-employee"
|
|
dense
|
|
outlined
|
|
v-model:employee-other="currentFromDataEmployee.employeeOtherInfo"
|
|
@undo="
|
|
() => {
|
|
if (
|
|
currentFromDataEmployee.employeeOtherInfo?.statusSave ===
|
|
false
|
|
) {
|
|
currentFromDataEmployee.employeeOtherInfo.statusSave = true;
|
|
}
|
|
}
|
|
"
|
|
@edit="
|
|
() => {
|
|
if (currentFromDataEmployee.employeeOtherInfo?.statusSave) {
|
|
currentFromDataEmployee.employeeOtherInfo.statusSave = false;
|
|
}
|
|
}
|
|
"
|
|
/>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</DialogForm>
|
|
|
|
<!-- NOTE: END - Employee Add Form -->
|
|
</template>
|
|
|
|
<style scoped>
|
|
:deep(i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon) {
|
|
color: hsl(var(--text-mute));
|
|
}
|
|
|
|
:deep(
|
|
i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-item__toggle-icon--rotated
|
|
) {
|
|
color: var(--brand-1);
|
|
}
|
|
|
|
:deep(.q-editor__toolbar-group):nth-child(2) {
|
|
margin-left: auto !important;
|
|
}
|
|
|
|
:deep(.q-editor__toolbar.row.no-wrap.scroll-x) {
|
|
background-color: var(--surface-2) !important;
|
|
}
|
|
|
|
:deep(.q-editor__toolbar) {
|
|
border-color: var(--surface-3) !important;
|
|
}
|
|
|
|
:deep(
|
|
.q-item.q-item-type.row.no-wrap.q-item--dense.q-item--clickable.q-link.cursor-pointer.q-focusable.q-hoverable.surface-1
|
|
.q-focus-helper
|
|
) {
|
|
visibility: hidden;
|
|
}
|
|
|
|
.selectable-item {
|
|
padding: 0;
|
|
appearance: none;
|
|
border: none;
|
|
background: transparent;
|
|
position: relative;
|
|
color: inherit;
|
|
|
|
& > .selectable-item__pos {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.selectable-item__selected {
|
|
& > :deep(*) {
|
|
border: 1px solid var(--_color, var(--brand-1)) !important;
|
|
}
|
|
|
|
& > .selectable-item__pos {
|
|
display: block;
|
|
position: absolute;
|
|
margin: var(--size-2);
|
|
right: 0;
|
|
top: 0;
|
|
border-radius: 50%;
|
|
width: 20px;
|
|
height: 20px;
|
|
color: var(--surface-1);
|
|
background: var(--brand-1);
|
|
color: white;
|
|
}
|
|
}
|
|
|
|
.selectable-item__disabled {
|
|
filter: grayscale(1);
|
|
opacity: 0.5;
|
|
|
|
& :deep(*) {
|
|
cursor: not-allowed;
|
|
}
|
|
}
|
|
</style>
|