hrms-recruit/src/modules/01_exam/components/Form/Information.vue
DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2f66878cab fix: header required
2025-11-19 16:19:24 +07:00

792 lines
26 KiB
Vue

<!-- card อมลสวนต -->
<template>
<div class="col-12 row justify-center">
<div class="row col-xs-12 col-sm-12 col-md-9 items-center q-col-gutter-x-sm q-col-gutter-y-xs">
<div class="col-12">
<HeaderTop
v-model:edit="edit"
header="ข้อมูลส่วนตัว"
icon="mdi-account"
:addData="true"
:editOnly="false"
:editData="false"
:required="true"
/>
</div>
<div class="col-xs-12 col-sm-3 col-md-4">
<q-field
:outlined="isStatusRegister"
bottom-slots
:stack-label="defaultInformation.cardid != null"
label="เลขประจำตัวประชาชน"
dense
:readonly="!isStatusRegister"
:class="getClass(isStatusRegister)"
:borderless="!isStatusRegister"
:rules="[
(val) =>
(val != null && val.length == 13) || `${'กรุณากรอกเลขประจำตัวประชาชนให้ถูกต้อง'}`,
(val) => /^[0-9]*$/.test(val) || `${'กรุณากรอกเลขประจำตัวประชาชนให้ถูกต้อง'}`
]"
v-model="defaultInformation.cardid"
>
<template v-slot:prepend>
<q-btn
round
flat
dense
size="md"
@click="idDialog"
v-if="status == 'register' || status == 'rejectRegister'"
>
<q-icon name="mdi-pencil-outline" color="primary" />
</q-btn>
</template>
<template v-slot:control>
<div class="self-center full-width no-outline" tabindex="0">
{{ defaultInformation.cardid }}
</div>
</template>
</q-field>
<!-- <q-input
:class="getClass(isStatusRegister)"
:outlined="isStatusRegister"
dense
type="tel"
mask="#############"
:counter="status == 'register' || status == 'rejectRegister' ? true : false"
lazy-rules
:readonly="!isStatusRegister"
:borderless="!isStatusRegister"
v-model="defaultInformation.cardid"
maxlength="13"
:rules="[
(val) => val.length == 13 || `${'กรุณากรอก เลขประจำตัวประชาชน'}`,
(val) => /^[0-9]*$/.test(val) || `${'กรุณากรอกเลขประจำตัวประชาชนให้ถูกต้อง'}`
]"
label="เลขบัตรประจำตัวประชาชน"
/> -->
</div>
<div class="col-xs-12 col-sm-3 col-md-2">
<!-- <q-select
:class="getClass(isStatusRegister)"
:readonly="!isStatusRegister"
:borderless="!isStatusRegister"
:rules="[(val) => !!val || `${'กรุณาเลือก คำนำหน้า'}`]"
:outlined="isStatusRegister"
dense
lazy-rules
v-model="defaultInformation.prefixId"
emit-value
map-options
option-label="name"
:options="prefixOptions"
option-value="id"
:label="`${'คำนำหน้า'}`"
@update:model-value="(value) => selectPrefix()"
/> -->
<q-select
:class="getClass(isStatusRegister)"
:readonly="!isStatusRegister"
:borderless="!isStatusRegister"
:rules="[(val) => !!val || `${'กรุณากรอก คำนำหน้า'}`]"
:outlined="isStatusRegister"
dense
lazy-rules
v-model="defaultInformation.prefix"
:label="`${'คำนำหน้า'}`"
:options="filteredPrefixOptions"
use-input
fill-input
input-debounce="0"
@filter="filterPrefix"
@input-value="setModel"
hide-selected
hide-dropdown-icon
/>
</div>
<div class="col-xs-12 col-sm-3 col-md-3">
<q-input
:class="getClass(isStatusRegister)"
:outlined="isStatusRegister"
dense
lazy-rules
:readonly="!isStatusRegister"
:borderless="!isStatusRegister"
v-model="defaultInformation.firstname"
:rules="[(val) => !!val || `${'กรุณากรอก ชื่อ'}`]"
:label="`${'ชื่อ (ภาษาไทย)'}`"
/>
</div>
<div class="col-xs-12 col-sm-3 col-md-3">
<q-input
:class="getClass(isStatusRegister)"
:outlined="isStatusRegister"
dense
lazy-rules
:readonly="!isStatusRegister"
:borderless="!isStatusRegister"
v-model="defaultInformation.lastname"
:rules="[(val) => !!val || `${'กรุณากรอก นามสกุล'}`]"
:label="`${'นามสกุล (ภาษาไทย)'}`"
/>
</div>
<div class="col-xs-12 col-sm-3 col-md-3">
<!-- :borderless="!false" -->
<q-select
:class="getClass(isStatusRegister)"
dense
:outlined="isStatusRegister"
:readonly="!isStatusRegister"
:borderless="!isStatusRegister"
lazy-rules
v-model="defaultInformation.nationality"
:options="opNat"
:rules="[(val) => !!val || `${'กรุณากรอก สัญชาติ'}`]"
:label="`${'สัญชาติ'}`"
/>
</div>
<div class="col-xs-12 col-sm-3 col-md-3">
<!-- <q-select
:class="getClass(isStatusRegister)"
:readonly="!isStatusRegister"
:borderless="!isStatusRegister"
:rules="[(val) => !!val || `${'กรุณาเลือก ศาสนา'}`]"
:outlined="isStatusRegister"
dense
lazy-rules
v-model="defaultInformation.religionId"
emit-value
map-options
option-label="name"
:options="religionOptions"
option-value="id"
:label="`${'ศาสนา'}`"
/> -->
<q-select
:class="getClass(isStatusRegister)"
:readonly="!isStatusRegister"
:borderless="!isStatusRegister"
:rules="[(val) => !!val || `${'กรุณากรอก ศาสนา'}`]"
:outlined="isStatusRegister"
dense
lazy-rules
v-model="defaultInformation.religionName"
:label="`${'ศาสนา'}`"
:options="filteredReligionOptions"
use-input
fill-input
input-debounce="0"
@filter="filterReligion"
@input-value="setModelReligionId"
hide-selected
hide-dropdown-icon
/>
</div>
<div class="col-xs-12 col-sm-3 col-md-3">
<datepicker
menu-class-name="display-modal"
v-model="defaultInformation.birthDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
:max-date="getMaxBirthDate(registerEndDate)"
:disabled="!(status == 'register' || status == 'rejectRegister')"
@update:modelValue="selectBirthDate"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
:class="getClass(isStatusRegister)"
:outlined="isStatusRegister"
dense
lazy-rules
:readonly="!isStatusRegister"
:borderless="!isStatusRegister"
:model-value="
defaultInformation.birthDate == null
? null
: date2Thai(defaultInformation.birthDate)
"
:rules="[(val) => !!val || `${'กรุณาเลือก วัน/เดือน/ปี เกิด'}`]"
:label="`${'วัน/เดือน/ปี เกิด'}`"
>
<template v-slot:prepend>
<q-icon
name="mdi-calendar-outline"
class="cursor-pointer"
size="22px"
:style="
status == 'register' || status == 'rejectRegister'
? 'color: var(--q-primary)'
: 'color: var(--q-grey)'
"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-12 col-sm-3 col-md-3">
<q-input
:class="getClass(isStatusRegister)"
dense
lazy-rules
bottom-slots
:outlined="isStatusRegister"
:readonly="!(status == 'rejectRegister')"
:borderless="!isStatusRegister"
:model-value="
defaultInformation.birthDate == null ? null : calAge(defaultInformation.birthDate)
"
:label="`${'อายุ'}`"
/>
</div>
<div class="col-xs-12 col-sm-3 col-md-3">
<q-input
:outlined="isStatusRegister"
dense
lazy-rules
:class="getClass(isStatusRegister)"
:readonly="!isStatusRegister"
:borderless="!isStatusRegister"
v-model="defaultInformation.tel"
:label="`${'โทรศัพท์'}`"
:rules="[
(val) => !!val || '* กรุณากรอกข้อมูลหมายเลขโทรศัพท์',
(val) =>
(val.length >= 9 && val.length <= 10 && val.startsWith('0')) ||
'กรุณากรอกข้อมูลหมายเลขโทรศัพท์ให้ถูกต้อง'
]"
:counter="isStatusRegister ? true : false"
type="tel"
mask="##########"
maxlength="10"
/>
</div>
<!-- <div class="col-xs-12 col-sm-3 col-md-3">
<q-input
:outlined="isStatusRegister"
dense
:counter="status == 'register' || status == 'rejectRegister' ? true : false"
lazy-rules
type="tel"
mask="##########"
maxlength="10"
:class="getClass(isStatusRegister)"
:readonly="!isStatusRegister"
:borderless="!isStatusRegister"
v-model="defaultInformation.phone"
:rules="[
(val) => val.length == 10 || `${'กรุณากรอก โทรศัพท์มือถือ'}`,
(val) => /^[0-9]*$/.test(val) || `${'กรุณากรอกข้อมูลโทรศัพท์มือถือให้ถูกต้อง'}`
]"
:label="`${'โทรศัพท์มือถือ'}`"
/>
</div> -->
<div class="col-xs-12 col-sm-3 col-md-3">
<!-- style="padding: 0 12px" -->
<q-input
:class="getClass(isStatusRegister)"
dense
lazy-rules
bottom-slots
:outlined="isStatusRegister"
:readonly="!(status == 'rejectRegister')"
:borderless="!isStatusRegister"
v-model="defaultInformation.email"
label="E-mail address"
/>
</div>
</div>
<div class="row col-xs-12 col-sm-6 col-md-3 justify-end">
<q-card bordered class="col-11 q-pa-sm justify-center text-center q-mt-sm">
<div class="text-weight-medium q-py-xs">อัปโหลดรูปถ่าย (รูปแนวตั้ง)</div>
<div class="containerimage row justify-center col-xs-6 col-sm-10 col-md-12">
<label for="file-upload" class="col-12 row justify-center">
<q-img v-if="img == ''" src="@/assets/avatar_user.jpg" class="col-6">
<div
class="overlay"
v-if="status == 'register' || status == 'rejectRegister' || status == 'done'"
>
<q-icon name="mdi-camera" />
<br />อัปเดต
</div>
</q-img>
<q-img v-else :src="img" class="col-12">
<div
class="overlay"
v-if="status == 'register' || status == 'rejectRegister' || status == 'done'"
>
<q-icon name="mdi-camera" />
<br />อัปเดต
</div>
</q-img>
</label>
<input id="file-upload" type="file" accept="image/*" @change="uploadImage" />
</div>
<div class="text-caption q-pt-sm text-grey-7">
<div>รูปถ่ายหน้าตรง ชุดสุภาพ ไม่สวมหมวก</div>
<div>ไม่ใส่แว่นตาดำ ไม่มีลวดลายใด ๆ บนรูป</div>
<div>ไฟล์ JPG ขนาด 40 -100 KB</div>
</div>
</q-card>
</div>
<!-- <div class="col-xs-12 col-sm-9 col-md-12">
<q-input
:class="getClass(isStatusRegister)"
:outlined="isStatusRegister"
dense
lazy-rules
:readonly="!isStatusRegister"
:borderless="!(status == 'register' || status == 'rejectRegister')"
v-model="defaultInformation.knowledge"
label="ความรความสามารถพเศษ"
type="textarea"
/>
</div> -->
</div>
<!-- dialog กรอกเลขบัตร -->
<q-dialog v-model="idModel">
<q-card style="width: 350px">
<q-card-section class="bg-grey-2 q-py-sm">
<div class="text-subtitle1 text-weight-medium">กรอกเลขประจำตัวประชาชน</div>
</q-card-section>
<q-separator color="grey-4" />
<q-card-section>
<q-form ref="myformcitizen">
<q-input
:class="getClass(true)"
:outlined="true"
dense
type="tel"
mask="#############"
:counter="true ? true : false"
lazy-rules
:readonly="!true"
:borderless="!true"
v-model="cardid1"
maxlength="13"
:rules="[
(val) => val.length == 13 || `${'กรุณากรอกเลขประจำตัวประชาชนให้ถูกต้อง'}`,
(val) => /^[0-9]*$/.test(val) || `${'กรุณากรอกเลขประจำตัวประชาชนให้ถูกต้อง'}`
]"
label="เลขบตรประจำตวประชาชน"
class="q-mb-sm"
/>
<q-input
:disable="cardid1 == null || cardid1.length < 13"
:class="getClass(true)"
:outlined="true"
dense
type="tel"
mask="#############"
:counter="true ? true : false"
lazy-rules
:readonly="!true"
:borderless="!true"
v-model="cardid2"
maxlength="13"
:rules="[
(val) => val.length == 13 || `${'กรุณากรอกเลขประจำตัวประชาชนให้ถูกต้อง'}`,
(val) => val == cardid1 || `${'เลขประจำตัวประชาชนไม่ตรงกัน'}`,
(val) => /^[0-9]*$/.test(val) || `${'กรุณากรอกเลขประจำตัวประชาชนให้ถูกต้อง'}`
]"
label="นยนเลขบตรประจำตวประชาชน"
/>
</q-form>
</q-card-section>
<q-separator color="grey-4" />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn flat label="ยกเล" color="red" v-close-popup />
<q-btn flat label="ตกลง" @click="checkCardId" class="bg-teal-1" />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { ref, onMounted, watch, computed } from 'vue'
import { useCounterMixin } from '@/stores/mixin'
import { useDataStore } from '@/stores/data'
import { useExamDataStore } from '@/modules/01_exam/store'
import http from '@/plugins/http'
import config from '@/app.config'
import type { PropType } from 'vue'
import { useRoute } from 'vue-router'
import keycloak from '@/plugins/keycloak'
import { useQuasar } from 'quasar'
import type { Information, DataOption } from '@/modules/01_exam/interface/index/Main'
import { defaultInformation, changeData } from '@/modules/01_exam/interface/index/Main'
import HeaderTop from '@/components/top.vue'
const storeExam = useExamDataStore()
const props = defineProps({
prefixOptions: {
type: Array as PropType<DataOption[]>,
required: true
},
religionOptions: {
type: Array as PropType<DataOption[]>,
required: true
},
provinceOptions: {
type: Array as PropType<DataOption[]>,
required: true
},
status: {
type: String,
required: true
},
form: {
type: Object,
required: true
},
isStatusRegister: {
type: Boolean,
required: true
}
})
const emit = defineEmits(['update:form'])
const $q = useQuasar()
const mixin = useCounterMixin()
const { date2Thai, calAge, success, messageError, notifyError, calAgeYear } = mixin
const dataStore = useDataStore()
const { loaderPage } = dataStore
const districtOptions = ref<DataOption[]>([])
const route = useRoute()
const examId = ref<string>(route.params.id.toString())
const positionId = ref<string>(route.params.positionId.toString())
const edit = ref<boolean>(true)
const myform = ref<any>({})
const myformcitizen = ref<any>({})
const img = ref<string>('')
const fileProfile = ref<File[]>([])
const cardid1 = ref<string | null>('')
const cardid2 = ref<string | null>('')
// const registerEndDate = ref<Date>(new Date())
const opNat = ref(['ไทย'])
const idModel = ref<boolean>(false)
watch(myform, async (count: any, prevCount: any) => {
emit('update:form', count)
})
watch(defaultInformation, async (count: Information, prevCount: Information) => {
await changeData('information', count)
})
onMounted(async () => {
await fetchData()
await fetchImgData()
if (defaultInformation.value.provinceId != null)
await fetchDistrict(defaultInformation.value.provinceId)
// candidateCheck()
})
const fetchData = async () => {
loaderPage(true)
await http
.get(config.API.candidateInformation(examId.value, positionId.value))
.then((res) => {
const data = res.data.result
defaultInformation.value.prefixId = data.prefixId
selectPrefix()
if (data.lastName == null || data.lastName == '') {
data.lastName = keycloak.tokenParsed == null ? '' : keycloak.tokenParsed.family_name
}
if (data.firstName == null || data.firstName == '') {
data.firstName = keycloak.tokenParsed == null ? '' : keycloak.tokenParsed.given_name
}
if (data.email == null || data.email == '') {
data.email = keycloak.tokenParsed == null ? '' : keycloak.tokenParsed.email
}
defaultInformation.value.lastname = data.lastName
defaultInformation.value.provinceId = data.citizenProvinceId
defaultInformation.value.districtId = data.citizenDistrictId
defaultInformation.value.birthDate =
data.dateOfBirth == null ? null : new Date(data.dateOfBirth)
defaultInformation.value.cardIdDate =
data.citizenDate == null ? null : new Date(data.citizenDate)
defaultInformation.value.cardid = data.citizenId
defaultInformation.value.firstname = data.firstName
defaultInformation.value.religionId = data.religionId
defaultInformation.value.nationality = data.nationality
defaultInformation.value.email = data.email
defaultInformation.value.phone = data.mobilePhone
defaultInformation.value.tel = data.telephone
defaultInformation.value.knowledge = data.knowledge
defaultInformation.value.prefix = data.prefix
defaultInformation.value.religionName = data.religion
})
.catch(() => {
defaultInformation.value.email =
keycloak.tokenParsed == null ? '' : keycloak.tokenParsed.email
defaultInformation.value.firstname =
keycloak.tokenParsed == null ? '' : keycloak.tokenParsed.given_name
defaultInformation.value.lastname =
keycloak.tokenParsed == null ? '' : keycloak.tokenParsed.family_name
})
.finally(() => {
loaderPage(false)
})
}
const checkCardId = async () => {
myformcitizen.value.validate().then(async (result: boolean) => {
if (result) {
idModel.value = false
await http
.put(config.API.candidateCheckCitizen(examId.value, positionId.value), {
citizenId: cardid2.value
})
.then((res) => {
success
defaultInformation.value.cardid = cardid2.value
})
.catch((e) => {
messageError($q, e)
})
.finally(async () => {})
return
} else {
notifyError($q, 'กรอกเลขบัตรประชาชนไม่ถูกต้อง')
}
})
}
const selectPrefix = async () => {
defaultInformation.value.prefix =
props.prefixOptions.filter((x) => x.id == defaultInformation.value.prefixId).length == 0
? ''
: props.prefixOptions.filter((x) => x.id == defaultInformation.value.prefixId)[0].name
}
const selectBirthDate = async () => {
if (defaultInformation.value.birthDate != null) {
if (calAgeYear(defaultInformation.value.birthDate, registerEndDate.value) < 18) {
defaultInformation.value.birthDate = null
notifyError($q, 'อายุไม่ถึง18')
} else if (calAgeYear(defaultInformation.value.birthDate, registerEndDate.value) > 60) {
defaultInformation.value.birthDate = null
notifyError($q, 'อายุเกิน60')
}
}
}
const registerEndDate = computed<Date>(() => {
return storeExam.examInfo?.registerEndDate == null
? new Date()
: new Date(storeExam.examInfo?.registerEndDate)
})
// const candidateCheck = async () => {
// loaderPage(true)
// await http
// .get(config.API.candidateCheckCreate(examId.value, positionId.value))
// .then(async (res) => {
// const data = res.data.result
// registerEndDate.value =
// data.registerEndDate == null ? new Date() : new Date(data.registerEndDate)
// })
// .catch((e) => {
// messageError($q, e)
// })
// .finally(() => {
// loaderPage(false)
// })
// }
const fetchImgData = async () => {
loaderPage(true)
await http
.get(config.API.candidateProfile(examId.value, positionId.value))
.then((res) => {
const data = res.data.result
img.value = data
defaultInformation.value.profileImg = data
})
.catch((e) => {
// messageError($q, e)
})
.finally(() => {
loaderPage(false)
})
}
const uploadImage = async (e: any) => {
let input = e.target.files
if (input.length > 0) {
const formData = new FormData()
formData.append('', input[0])
loaderPage(true)
await http
.put(config.API.candidateProfile(examId.value, positionId.value), formData)
.then((res) => {
success($q, 'อัพโหลดรูปสำเร็จ')
})
.catch((e) => {
messageError($q, e)
})
.finally(async () => {
await fetchImgData()
fileProfile.value = []
})
return
}
}
const selectProvince = async (val: string) => {
defaultInformation.value.districtId = ''
myform.value.resetValidation()
await fetchDistrict(val)
}
const fetchDistrict = async (id: string) => {
loaderPage(true)
await http
.get(config.API.listDistrict(id))
.then((res) => {
const data = res.data.result.districts
let option: DataOption[] = []
data.map((r: DataOption) => {
option.push({ id: r.id.toString(), name: r.name.toString() })
})
districtOptions.value = option
})
.catch((e) => {
messageError($q, e)
})
.finally(() => {
loaderPage(false)
})
}
const getClass = (val: boolean) => {
return {
'full-width inputgreen cursor-pointer': val,
'full-width cursor-pointer': !val
}
}
const idDialog = () => {
idModel.value = true
cardid2.value = defaultInformation.value.cardid
cardid1.value = defaultInformation.value.cardid
}
function getMaxBirthDate(registerEndDate: Date | string): Date {
const date = new Date(registerEndDate)
date.setFullYear(date.getFullYear() - 18)
return date
}
const filteredPrefixOptions = ref<string[]>([])
const filterPrefix = (val: string, update: (fn: () => void) => void) => {
update(() => {
if (val === '') {
filteredPrefixOptions.value = []
} else {
const needle = val.toLowerCase()
filteredPrefixOptions.value = props.prefixOptions
.filter((v) => v.name.toLowerCase().indexOf(needle) > -1)
.map((v) => v.name)
}
})
}
const setModel = (val: string) => {
defaultInformation.value.prefix = val
}
const filteredReligionOptions = ref<string[]>([])
const filterReligion = (val: string, update: (fn: () => void) => void) => {
update(() => {
if (val === '') {
filteredReligionOptions.value = []
} else {
const needle = val.toLowerCase()
filteredReligionOptions.value = props.religionOptions
.filter((v) => v.name.toLowerCase().indexOf(needle) > -1)
.map((v) => v.name)
}
})
}
const setModelReligionId = (val: string) => {
defaultInformation.value.religionName = val
}
</script>
<style>
.display-modal {
display: block !important;
position: absolute !important;
z-index: 99999 !important;
top: 70% !important;
overflow: visible !important;
}
.containerimage {
position: relative;
width: 100%;
}
.q-img {
display: block;
width: 100%;
max-height: 170px;
padding: 1%;
border-radius: 8px;
border: solid 2px rgba(168, 168, 168, 0.055) !important;
box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.048), 0 3px 6px 0 rgba(0, 0, 0, 0.097);
}
.overlay {
position: absolute;
bottom: 0;
background: rgb(0, 0, 0);
background: rgba(0, 0, 0, 0.7);
color: #f1f1f1;
transition: 0.5s ease;
width: 100%;
height: 70px;
opacity: 0;
color: white;
text-align: center;
cursor: pointer;
padding: 5% 0 5% 0;
}
.containerimage:hover .overlay {
opacity: 1;
}
.q-field__bottom {
padding: 5px 10px 0px 0px;
}
input[type='file'] {
display: none;
}
.custom-file-upload {
border: 1px solid #ccc;
display: inline-block;
padding: 6px 12px;
cursor: pointer;
}
</style>