//
This commit is contained in:
parent
a27eda7124
commit
819a82cd89
@ -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>
|
||||||
@ -61,8 +61,46 @@
|
|||||||
이미 사용 중인 전화번호입니다.
|
이미 사용 중인 전화번호입니다.
|
||||||
</span>
|
</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>
|
||||||
|
|
||||||
|
<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),
|
||||||
|
phone: user.phone || '',
|
||||||
|
color: colorCode,
|
||||||
|
mbti: user.mbit || '',
|
||||||
address: {
|
address: {
|
||||||
address: user.address || '',
|
address: user.address || '',
|
||||||
detailAddress: user.addressDetail || '',
|
detailAddress: user.addressDetail || '',
|
||||||
postcode: user.zipcode || ''
|
postcode: user.zipcode || ''
|
||||||
},
|
}
|
||||||
phone: user.phone || '',
|
};
|
||||||
color: colorCode,
|
form.value = { ...initData };
|
||||||
mbti: user.mbit || ''
|
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>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user