localhost-front/src/components/user/RegisterForm.vue
dyhj625 4f9a879083
All checks were successful
LocalNet_front/pipeline/head This commit looks good
.
2025-04-08 14:37:13 +09:00

459 lines
16 KiB
Vue

<template>
<form @submit.prevent="handleSubmit">
<div class="text-center">
<label
for="profilePic"
class="rounded-circle m-auto ui-bg-cover position-relative cursor-pointer "
id="profileLabel"
style="width: 100px; height: 100px; background-image: url(img/avatars/default-Profile.jpg); background-repeat: no-repeat; background-size: cover;"
>
</label>
<span class="link-danger position-absolute">*</span>
<input type="file" id="profilePic" class="d-none" @change="profileUpload" />
<span v-if="profilerr" class="invalid-feedback d-block">{{ profilerr }}</span>
</div>
<div class="col-xl-12">
<UserFormInput
title="아이디"
name="id"
:isEssential="true"
:is-alert="idAlert"
:useInputGroup="true"
@update:data="id = $event"
@update:alert="idAlert = $event"
@blur="checkIdDuplicate"
:value="id"
/>
<span v-if="idError" class="invalid-feedback d-block">{{ idError }}</span>
<UserFormInput
title="비밀번호"
name="pw"
type="password"
:isEssential="true"
:is-alert="passwordAlert"
@update:data="password = $event"
@update:alert="passwordAlert = $event"
:value="password"
/>
<span v-if="passwordError" class="invalid-feedback d-block">{{ passwordError }}</span>
<UserFormInput
title="비밀번호 확인"
name="pwch"
type="password"
:isEssential="true"
:is-alert="passwordcheckAlert"
@update:data="passwordcheck = $event"
@update:alert="passwordcheckAlert = $event"
:value="passwordcheck"
/>
<span v-if="passwordcheckError" class="invalid-feedback d-block">{{ passwordcheckError }}</span>
<FormSelect
title="비밀번호 힌트"
name="pwhint"
:is-essential="true"
:is-row="false"
:is-label="true"
:is-common="true"
:data="pwhintList"
@update:data="pwhint = $event"
/>
<UserFormInput
title="답변"
name="pwhintRes"
:is-essential="true"
:is-alert="pwhintResAlert"
@update:data="pwhintRes = $event"
@update:alert="pwhintResAlert = $event"
:value="pwhintRes"
/>
<div class="d-flex">
<UserFormInput
title="이름"
name="name"
:is-essential="true"
:is-alert="nameAlert"
@update:data="name = $event"
@update:alert="nameAlert = $event"
:value="name"
class="me-2 w-50"
/>
<FormSelect
title="컬러"
name="color"
:is-essential="true"
:is-row="false"
:is-label="true"
:is-common="true"
:is-color="true"
:data="colorList"
@update:data="handleColorUpdate"
:is-alert="colorAlert"
class="w-50"
/>
</div>
<span v-if="colorError" class="w-50 ps-1 ms-auto invalid-feedback d-block">{{ colorError }}</span>
<div class="d-flex">
<UserFormInput
title="생년월일"
name="birth"
:type="'date'"
:is-essential="true"
:is-alert="birthAlert"
@update:data="birth = $event"
@update:alert="birthAlert = $event"
:value="birth"
class="me-2 w-50"
/>
<FormSelect
title="MBTI"
name="mbti"
:is-essential="true"
:is-row="false"
:is-label="true"
:is-common="true"
:is-mbti="true"
:data="mbtiList"
@update:data="mbti = $event"
class="w-50"
/>
</div>
<ArrInput
title="주소"
name="address"
:isEssential="true"
:is-alert="addressAlert"
@update:data="handleAddressUpdate"
@update:alert="addressAlert = $event"
:value="address"
:disabled="true"
/>
<UserFormInput
title="휴대전화"
name="phone"
:isEssential="true"
:is-alert="phoneAlert"
@update:alert="phoneAlert = $event"
@blur="checkPhoneDuplicate"
:maxlength="11"
:value="phone"
@keypress="onlyNumber"
@input="inputEvent"
/>
<span v-if="phoneError" class="invalid-feedback d-block">{{ phoneError }}</span>
<div class="d-flex mt-5">
<RouterLink type="button" class="btn btn-secondary me-2 w-50" to="/login">취소</RouterLink>
<button type="submit" class="btn btn-primary w-50">등록신청</button>
</div>
</div>
</form>
</template>
<script setup>
import { ref, watch } from 'vue';
import $api from '@api';
import commonApi from '@/common/commonApi';
import UserFormInput from '@c/input/UserFormInput.vue';
import FormSelect from '@c/input/FormSelect.vue';
import ArrInput from '@c/input/ArrInput.vue';
import { fileMsg } from '@/common/msgEnum';
import { useRouter } from 'vue-router';
import { useToastStore } from '@s/toastStore';
const router = useRouter();
const profile = ref(null);
const profilerr = ref('');
const id = ref('');
const idError = ref('');
const password = ref('');
const passwordError = ref('');
const passwordcheck = ref('');
const passwordcheckError = ref('');
const pwhintRes = ref('');
const name = ref('');
const birth = ref('');
const address = ref('');
const detailAddress = ref('');
const postcode = ref(''); // 우편번호
const phone = ref('');
const phoneError = ref('');
const color = ref(''); // 선택된 color
const colorError = ref('');
const mbti = ref(''); // 선택된 MBTI
const pwhint = ref(''); // 선택된 pwhint
const profilAlert = ref(false);
const idAlert = ref(false);
const idErrorAlert = ref(false);
const passwordAlert = ref(false);
const passwordErrorAlert = ref(false);
const passwordcheckAlert = ref(false);
const passwordcheckErrorAlert = ref(false); // 비밀번호 확인 오류 메시지
const pwhintResAlert = ref(false);
const nameAlert = ref(false);
const colorAlert = ref(false);
const birthAlert = ref(false);
const addressAlert = ref(false);
const phoneAlert = ref(false);
const phoneErrorAlert = ref(false);
const colorErrorAlert = ref(false);
const toastStore = useToastStore();
// 프로필 체크
const profileValid = (size, type) => {
const maxSize = 5 * 1024 * 1024;
const validTypes = ['image/jpeg', 'image/png'];
// 용량
if (size > maxSize) {
profilerr.value = fileMsg.FileMaxSizeMsg;
return false;
}
// 파일 타입
if (!validTypes.includes(type)) {
profilerr.value = fileMsg.FileNotTypeMsg;
return false;
}
profilerr.value = '';
return true;
};
// 파일 업로드 시 미리보기
const profileUpload = e => {
const file = e.target.files[0];
const profileLabel = document.getElementById('profileLabel');
// 사이즈, 파일 타입 안 맞으면 기본 이미지
if (!profileValid(file.size, file.type)) {
e.target.value = '';
profileLabel.style.backgroundImage = 'url("img/avatars/default-Profile.jpg")';
return false;
}
const profilePic = e.target;
const url = URL.createObjectURL(profilePic.files[0]);
profileLabel.style.backgroundImage = `url(${url})`;
profile.value = file;
};
// 아이디 체크
const checkIdDuplicate = async () => {
// 길이 검증 먼저 수행
if (id.value.length < 4) {
idError.value = '아이디는 4자리 이상이어야 합니다.';
idErrorAlert.value = true;
return;
}
const response = await $api.get(`/user/checkId?memberIds=${id.value}`);
if (!response.data.data) {
idErrorAlert.value = true;
idError.value = '이미 사용 중인 아이디입니다.';
} else {
idErrorAlert.value = false;
idError.value = '';
}
};
// 전화번호 중복체크
const checkPhoneDuplicate = async () => {
const response = await $api.get(`/user/checkPhone?memberTel=${phone.value}`);
if (!response.data.data) {
phoneErrorAlert.value = true;
phoneError.value = '이미 사용 중인 전화번호입니다.';
} else {
phoneErrorAlert.value = false;
phoneError.value = '';
}
};
// 컬러, mbti, 비밀번호 힌트 목록 불러오기
const { colorList, mbtiList, pwhintList } = commonApi({
loadColor: true,
colorType: 'YON',
loadMbti: true,
loadPwhint: true,
});
// 주소 업데이트 핸들러
const handleAddressUpdate = addressData => {
address.value = addressData.address;
detailAddress.value = addressData.detailAddress;
postcode.value = addressData.postcode; // 우편번호
};
// 색상 중복체크
const checkColorDuplicate = async () => {
const response = await $api.get(`/user/checkColor?memberCol=${color.value}`);
if (response.data.data) {
colorErrorAlert.value = true;
colorError.value = '이미 사용 중인 색상입니다.';
} else {
colorErrorAlert.value = false;
colorError.value = '';
}
};
const handleColorUpdate = async newColor => {
color.value = newColor;
colorError.value = '';
colorErrorAlert.value = false;
await checkColorDuplicate();
}
const onlyNumber = (event) => {
// 숫자가 아니면 입력 차단
if (!/^[0-9]$/.test(event.key)) {
event.preventDefault();
}
};
const inputEvent = (e) => {
const newValue = e.target.value.replace(/\D/g, ''); // 숫자만 남김
e.target.value = newValue; // 입력 필드 즉시 반영
phone.value = newValue; // Vue 반응형 상태 업데이트
};
watch(id, (newValue) => {
if (newValue && newValue.length >= 4) {
idError.value = '';
idErrorAlert.value = false;
} else if (newValue && newValue.length < 4) {
idError.value = '아이디는 4자리 이상이어야 합니다.';
idErrorAlert.value = true;
}
});
watch(password, (newValue) => {
if (newValue && newValue.length >= 4) {
passwordErrorAlert.value = false;
passwordError.value = '';
} else if (newValue && newValue.length < 4) {
passwordErrorAlert.value = true;
passwordError.value = '비밀번호는 4자리 이상이어야 합니다.';
}
});
// 비밀번호 확인
watch([password, passwordcheck], ([newPassword, newPasswordCheck]) => {
if (newPassword && newPasswordCheck) {
if (newPassword !== newPasswordCheck) {
passwordcheckError.value = '비밀번호가 일치하지 않습니다.';
passwordcheckErrorAlert.value = true;
} else {
passwordcheckError.value = '';
passwordcheckErrorAlert.value = false;
}
}
});
// 회원가입
const handleSubmit = async () => {
await checkColorDuplicate();
idAlert.value = id.value.trim() === '';
passwordAlert.value = password.value.trim() === '';
passwordcheckAlert.value = passwordcheck.value.trim() === '';
pwhintResAlert.value = pwhintRes.value.trim() === '';
nameAlert.value = name.value.trim() === '';
birthAlert.value = birth.value.trim() === '';
addressAlert.value = address.value.trim() === '';
phoneAlert.value = phone.value.trim() === '';
if (!colorList.value || colorList.value.length === 0) {
colorAlert.value = true;
}
// 아이디 길이 체크
if (id.value && id.value.length < 4) {
idErrorAlert.value = true;
idError.value = '아이디는 4자리 이상이어야 합니다.';
}
// 비밀번호 길이 체크
if (password.value && password.value.length < 4) {
passwordErrorAlert.value = true;
passwordError.value = '비밀번호는 4자리 이상이어야 합니다.';
} else {
passwordError.value = '';
}
if (!/^\d+$/.test(phone.value)) {
phoneAlert.value = true;
} else {
phoneAlert.value = false;
}
// 프로필 이미지 체크
if (!profile.value) {
profilerr.value = '프로필 이미지를 선택해주세요.';
profilAlert.value = true;
} else {
profilerr.value = '';
profilAlert.value = false;
}
if (
profilAlert.value ||
idAlert.value ||
idErrorAlert.value ||
passwordAlert.value ||
passwordErrorAlert.value ||
passwordcheckAlert.value ||
passwordcheckErrorAlert.value ||
pwhintResAlert.value ||
nameAlert.value ||
birthAlert.value ||
addressAlert.value ||
phoneAlert.value ||
phoneErrorAlert.value ||
colorAlert.value ||
colorErrorAlert.value
) {
return;
}
const formData = new FormData();
formData.append('memberIds', id.value);
formData.append('memberPwd', password.value);
formData.append('memberPwh', pwhint.value);
formData.append('memberPwr', pwhintRes.value);
formData.append('memberNam', name.value);
formData.append('memberArr', address.value);
formData.append('memberDtl', detailAddress.value);
formData.append('memberZip', postcode.value);
formData.append('memberBth', birth.value);
formData.append('memberTel', phone.value);
formData.append('memberCol', color.value);
formData.append('memberMbt', mbti.value);
formData.append('memberPrf', profile.value);
const response = await $api.post('/user/join', formData, { isFormData: true });
if (response.status === 200) {
toastStore.onToast('등록신청이 완료되었습니다. 관리자 승인 후 이용가능합니다.', 's');
router.push('/login');
}
};
</script>