This commit is contained in:
dyhj625 2025-04-04 12:56:53 +09:00
parent a27eda7124
commit 819a82cd89

View File

@ -38,7 +38,7 @@
:value="form.color" :value="form.color"
@update:data="handleColorUpdate" @update:data="handleColorUpdate"
/> />
<span v-if="colorDuplicated" class="text-red-500 invalid-feedback mt-1 d-block"> <span v-if="colorDuplicated" class="text-danger invalid-feedback mt-1 d-block">
이미 사용 중인 컬러입니다. 이미 사용 중인 컬러입니다.
</span> </span>
</div> </div>
@ -57,12 +57,50 @@
<UserFormInput title="전화번호" name="phone" :value="form.phone" <UserFormInput title="전화번호" name="phone" :value="form.phone"
@update:data="form.phone = $event" @blur="checkPhoneDuplicate" @update:data="form.phone = $event" @blur="checkPhoneDuplicate"
:maxlength="11" @keypress="onlyNumber" /> :maxlength="11" @keypress="onlyNumber" />
<span v-if="phoneDuplicated" class="text-danger invalid-feedback mt-1 d-block"> <span v-if="phoneDuplicated" class="text-danger invalid-feedback mt-1 d-block">
이미 사용 중인 전화번호입니다. 이미 사용 중인 전화번호입니다.
</span>
<!-- 기존 비밀번호 입력 -->
<UserFormInput title="기존 비밀번호" name="currentPw" type="password"
:value="password.current" @update:data="password.current = $event"
@blur="checkCurrentPassword" />
<span v-if="passwordError" class="text-danger invalid-feedback mt-1 d-block">
비밀번호가 일치하지 않습니다.
</span>
<!-- 비밀번호 재설정 -->
<div v-if="showResetPw">
<UserFormInput title="새 비밀번호" name="newPw" type="password"
:value="password.new" @update:data="password.new = $event" />
<span v-if="password.new && password.new.length < 4"
class="text-danger invalid-feedback mt-1 d-block">
비밀번호는 최소 4자리 이상이어야 합니다.
</span>
<span v-if="password.new === password.current"
class="text-danger invalid-feedback mt-1 d-block">
기존 비밀번호와 다르게 설정해주세요.
</span> </span>
<UserFormInput title="비밀번호 확인" name="confirmPw" type="password"
:value="password.confirm" @update:data="password.confirm = $event" />
<span v-if="password.confirm && password.confirm !== password.new"
class="text-danger invalid-feedback mt-1 d-block">
비밀번호와 일치하지 않습니다.
</span>
<div class="d-flex justify-content-end mt-2">
<button type="button" class="btn btn-sm btn-outline-primary"
:disabled="!canResetPassword"
@click="handlePasswordReset">
비밀번호 변경
</button>
</div>
</div>
<div class="d-flex mt-5"> <div class="d-flex mt-5">
<button type="submit" class="btn btn-primary w-100" :disabled="!isChanged || phoneDuplicated || colorDuplicated"> <button type="submit" class="btn btn-primary w-100"
:disabled="!isChanged || phoneDuplicated || colorDuplicated">
정보 수정 정보 수정
</button> </button>
</div> </div>
@ -73,7 +111,7 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, computed } from 'vue'; import { ref, computed, onMounted } from 'vue';
import $api from '@api'; import $api from '@api';
import UserFormInput from '@c/input/UserFormInput.vue'; import UserFormInput from '@c/input/UserFormInput.vue';
import FormSelect from '@c/input/FormSelect.vue'; import FormSelect from '@c/input/FormSelect.vue';
@ -83,12 +121,9 @@ import { useToastStore } from '@s/toastStore';
const toastStore = useToastStore(); const toastStore = useToastStore();
const form = ref({ const form = ref({
entryDate: '', entryDate: '', birth: '', phone: '', color: '', mbti: '',
birth: '', address: { address: '', detailAddress: '', postcode: '' },
address: { address: '', detailAddress: '', postcode: '' }, id: ''
phone: '',
color: '',
mbti: ''
}); });
const originalData = ref({}); const originalData = ref({});
const profile = ref(''); const profile = ref('');
@ -101,164 +136,161 @@ const phoneDuplicated = ref(false);
const mbtiList = ref([]); const mbtiList = ref([]);
const colorList = ref([]); const colorList = ref([]);
const password = ref({ current: '', new: '', confirm: '' });
const passwordError = ref(false);
const showResetPw = ref(false);
const canResetPassword = computed(() => {
return (
password.value.new.length >= 4 &&
password.value.new !== password.value.current &&
password.value.new === password.value.confirm
);
});
const isChanged = computed(() => { const isChanged = computed(() => {
const f = form.value; const f = form.value;
const o = originalData.value; const o = originalData.value;
return ( return (
f.entryDate !== o.entryDate || f.entryDate !== o.entryDate || f.birth !== o.birth || f.phone !== o.phone ||
f.birth !== o.birth || f.color !== o.color || f.mbti !== o.mbti || profileChanged.value ||
f.phone !== o.phone ||
f.color !== o.color ||
f.mbti !== o.mbti ||
f.address.address !== o.address.address || f.address.address !== o.address.address ||
f.address.detailAddress !== o.address.detailAddress || f.address.detailAddress !== o.address.detailAddress ||
f.address.postcode !== o.address.postcode || f.address.postcode !== o.address.postcode
profileChanged.value );
);
}); });
const baseUrl = $api.defaults.baseURL.replace(/api\/$/, ''); const baseUrl = $api.defaults.baseURL.replace(/api\/$/, '');
const defaultProfile = "img/avatars/default-Profile.jpg"; const defaultProfile = "img/avatars/default-Profile.jpg";
const getProfileImageUrl = (fileName) =>
const getProfileImageUrl = (fileName) => { fileName?.trim() ? `${baseUrl}upload/img/profile/${fileName}?t=${Date.now()}` : defaultProfile;
return fileName?.trim()
? `${baseUrl}upload/img/profile/${fileName}?t=${Date.now()}`
: defaultProfile;
};
const profilePreviewStyle = computed(() => ({ const profilePreviewStyle = computed(() => ({
width: '100px', width: '100px',
height: '100px', height: '100px',
backgroundImage: `url(${profile.value})`, backgroundImage: `url(${profile.value})`,
backgroundRepeat: 'no-repeat', backgroundRepeat: 'no-repeat',
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center' backgroundPosition: 'center'
})); }));
const profileUpload = (e) => { const profileUpload = (e) => {
const file = e.target.files[0]; const file = e.target.files[0];
if (!file) return; if (!file) return;
if (file.size > 5 * 1024 * 1024 || !['image/jpeg', 'image/png'].includes(file.type)) { if (file.size > 5 * 1024 * 1024 || !['image/jpeg', 'image/png'].includes(file.type)) {
profilerr.value = '5MB 이하의 JPG/PNG 파일만 업로드 가능합니다.'; profilerr.value = '5MB 이하의 JPG/PNG 파일만 업로드 가능합니다.';
return; return;
} }
profilerr.value = ''; profilerr.value = '';
if (currentBlobUrl.value) URL.revokeObjectURL(currentBlobUrl.value); if (currentBlobUrl.value) URL.revokeObjectURL(currentBlobUrl.value);
uploadedFile.value = file; uploadedFile.value = file;
const newBlobUrl = URL.createObjectURL(file); const newBlobUrl = URL.createObjectURL(file);
profile.value = newBlobUrl; profile.value = newBlobUrl;
currentBlobUrl.value = newBlobUrl; currentBlobUrl.value = newBlobUrl;
profileChanged.value = true; profileChanged.value = true;
}; };
const onlyNumber = (e) => { const onlyNumber = (e) => {
if (!/[0-9]/.test(e.key)) e.preventDefault(); if (!/[0-9]/.test(e.key)) e.preventDefault();
}; };
const checkPhoneDuplicate = async () => { const checkPhoneDuplicate = async () => {
const currentPhone = form.value.phone; const currentPhone = form.value.phone;
if (currentPhone === originalData.value.phone) { phoneDuplicated.value = currentPhone !== originalData.value.phone &&
phoneDuplicated.value = false; !(await $api.get('/user/checkPhone', { params: { memberTel: currentPhone } })).data.data;
return;
}
const res = await $api.get('/user/checkPhone', { params: { memberTel: form.value.phone } });
phoneDuplicated.value = !res.data.data;
}; };
const handleColorUpdate = async (colorVal) => { const handleColorUpdate = async (colorVal) => {
form.value.color = colorVal; form.value.color = colorVal;
if (colorVal !== originalData.value.color) { colorDuplicated.value = colorVal !== originalData.value.color &&
const res = await $api.get('/user/checkColor', { (await $api.get('/user/checkColor', { params: { memberCol: colorVal } })).data.data;
params: { memberCol: colorVal }
});
colorDuplicated.value = res.data.data; // true
} else {
colorDuplicated.value = false;
}
}; };
const formatDate = (isoDate) => (isoDate ? isoDate.split('T')[0] : ''); const checkCurrentPassword = async () => {
if (!password.value.current) return;
const res = await $api.post('/user/checkPassword', {
id: form.value.id,
password: password.value.current
});
passwordError.value = res.data.data;
showResetPw.value = !res.data.data;
};
const handlePasswordReset = async () => {
const res = await $api.patch('/user/pwNew', {
id: form.value.id,
password: password.value.new
});
if (res.data.data) {
toastStore.onToast('비밀번호가 변경되었습니다.', 's');
password.value = { current: '', new: '', confirm: '' };
showResetPw.value = false;
passwordError.value = false;
} else {
toastStore.onToast('비밀번호 변경 실패', 'e');
}
};
const formatDate = (isoDate) => isoDate?.split('T')[0] || '';
const loadInitialData = async () => { const loadInitialData = async () => {
const userRes = await $api.get('/user/userInfo'); const user = (await $api.get('/user/userInfo')).data.data;
const user = userRes.data.data; const serverColors = (await $api.get('/user/color', { params: { type: 'YON' } })).data.data.map(c => ({
value: c.CMNCODVAL, label: c.CMNCODNAM
}));
const matchedColor = serverColors.find(c => c.label === user.usercolor);
const colorCode = matchedColor ? matchedColor.value : user.color;
colorList.value = serverColors.some(c => c.value === colorCode)
? serverColors
: [{ value: colorCode, label: user.usercolor }, ...serverColors];
const colorRes = await $api.get('/user/color', { params: { type: 'YON' } }); const initData = {
const serverColors = colorRes.data.data.map(c => ({ id: user.loginId,
value: c.CMNCODVAL,
label: c.CMNCODNAM
}));
const matchedColor = serverColors.find(c => c.label === user.usercolor);
const colorCode = matchedColor ? matchedColor.value : user.color;
const exists = serverColors.some(c => c.value === colorCode);
colorList.value = exists ? serverColors : [{ value: colorCode, label: user.usercolor }, ...serverColors];
const initData = {
entryDate: formatDate(user.isCdt), entryDate: formatDate(user.isCdt),
birth: formatDate(user.birth), birth: formatDate(user.birth),
address: {
address: user.address || '',
detailAddress: user.addressDetail || '',
postcode: user.zipcode || ''
},
phone: user.phone || '', phone: user.phone || '',
color: colorCode, color: colorCode,
mbti: user.mbit || '' mbti: user.mbit || '',
}; address: {
address: user.address || '',
detailAddress: user.addressDetail || '',
postcode: user.zipcode || ''
}
};
form.value = { ...initData };
originalData.value = { ...initData };
profile.value = getProfileImageUrl(user.profile);
profileChanged.value = false;
form.value = { ...initData }; const mbtiRes = await $api.get('/user/mbti');
originalData.value = { ...initData }; mbtiList.value = mbtiRes.data.data.map(m => ({ value: m.CMNCODVAL, label: m.CMNCODNAM }));
profile.value = getProfileImageUrl(user.profile);
profileChanged.value = false;
const mbtiRes = await $api.get('/user/mbti');
mbtiList.value = mbtiRes.data.data.map(m => ({
value: m.CMNCODVAL,
label: m.CMNCODNAM
}));
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
const formData = new FormData(); const formData = new FormData();
formData.append('entryDate', form.value.entryDate); Object.entries(form.value).forEach(([k, v]) => {
formData.append('birth', form.value.birth); if (typeof v === 'object') {
formData.append('phone', form.value.phone); formData.append('address', v.address);
formData.append('color', form.value.color); formData.append('detailAddress', v.detailAddress);
formData.append('mbti', form.value.mbti); formData.append('postcode', v.postcode);
formData.append('address', form.value.address.address); } else {
formData.append('detailAddress', form.value.address.detailAddress); formData.append(k, v);
formData.append('postcode', form.value.address.postcode);
if (uploadedFile.value) formData.append('profileFile', uploadedFile.value);
//
if (form.value.color !== originalData.value.color) {
if (form.value.color) {
await $api.patch('/user/updateColorYon', {
color: form.value.color,
type: 'YON'
});
}
if (originalData.value.color) {
await $api.patch('/user/updateColorChange', {
color: originalData.value.color,
type: 'YON'
});
}
} }
});
if (uploadedFile.value) formData.append('profileFile', uploadedFile.value);
await $api.patch('/user/updateInfo', formData, { isFormData: true }); if (form.value.color !== originalData.value.color) {
if (form.value.color) await $api.patch('/user/updateColorYon', { color: form.value.color, type: 'YON' });
if (originalData.value.color) await $api.patch('/user/updateColorChange', { color: originalData.value.color, type: 'YON' });
}
originalData.value = { ...form.value }; await $api.patch('/user/updateInfo', formData, { isFormData: true });
profileChanged.value = false; originalData.value = { ...form.value };
location.reload(); profileChanged.value = false;
toastStore.onToast('정보가 수정되었습니다.', 's'); location.reload();
toastStore.onToast('정보가 수정되었습니다.', 's');
}; };
onMounted(() => { onMounted(() => loadInitialData());
loadInitialData();
});
</script> </script>
<style scoped> <style scoped>