수정사항
This commit is contained in:
parent
44cec4cccd
commit
eb39a2a0b7
@ -811,4 +811,10 @@ input:checked + .slider:before {
|
||||
}
|
||||
.mr-1{
|
||||
margin-right: 0.25rem !important;
|
||||
}
|
||||
|
||||
.nickname-ellipsis {
|
||||
white-space: nowrap;
|
||||
max-width: 100px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@ -41,6 +41,8 @@
|
||||
v-model="nickname"
|
||||
placeholder="닉네임"
|
||||
@input="clearAlert('nickname')"
|
||||
@keypress="noSpace"
|
||||
:maxlength="6"
|
||||
/>
|
||||
<!-- 닉네임 경고 메시지 -->
|
||||
<div v-if="nicknameAlert" class="position-absolute text-danger small top-100 start-0">
|
||||
@ -109,6 +111,10 @@
|
||||
},
|
||||
});
|
||||
|
||||
const noSpace = (e) => {
|
||||
if (e.key === ' ') e.preventDefault();
|
||||
};
|
||||
|
||||
const $common = inject('common');
|
||||
const comment = ref('');
|
||||
const password = ref('');
|
||||
|
||||
@ -44,11 +44,17 @@
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<!-- 익명게시판은 'nickname', 나머지는 'writer' -->
|
||||
<th class="text-center" style="width: 20%;">
|
||||
<!-- 익명게시판은 '닉네임', 나머지는 '작성자' -->
|
||||
<th class="text-start">
|
||||
<div class="ms-4">
|
||||
{{ selectedBoard === 'anonymous' ? '닉네임' : '작성자' }}
|
||||
</div>
|
||||
</th>
|
||||
<th class="text-start" style="width: 65%;">
|
||||
<div class="ms-4">
|
||||
제목
|
||||
</div>
|
||||
</th>
|
||||
<th class="text-center" style="width: 65%;">제목</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -58,11 +64,13 @@
|
||||
style="cursor: pointer;"
|
||||
@click="goDetail(post.id, selectedBoard)"
|
||||
>
|
||||
<td class="text-center small">
|
||||
<td class="text-start nickname-ellipsis small">
|
||||
<div class="ms-4">
|
||||
{{ selectedBoard === 'anonymous' ? post.nickname : post.author }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-start fs-6">
|
||||
<div class="ms-2">
|
||||
<div class="ms-4">
|
||||
{{ truncateTitle(post.title) }}
|
||||
<span v-if="post.commentCount" class="text-danger ml-1 small">
|
||||
[{{ post.commentCount }}]
|
||||
@ -120,97 +128,97 @@ const anonymousList = ref([]);
|
||||
|
||||
// 선택된 게시판에 따른 미리보기 목록 computed
|
||||
const currentList = computed(() => {
|
||||
if (selectedBoard.value === 'notices') return noticeList.value;
|
||||
if (selectedBoard.value === 'general') return freeList.value;
|
||||
if (selectedBoard.value === 'anonymous') return anonymousList.value;
|
||||
return [];
|
||||
if (selectedBoard.value === 'notices') return noticeList.value;
|
||||
if (selectedBoard.value === 'general') return freeList.value;
|
||||
if (selectedBoard.value === 'anonymous') return anonymousList.value;
|
||||
return [];
|
||||
});
|
||||
|
||||
// 날짜 포맷 함수: 오늘이면 HH:mm, 아니면 YYYY-MM-DD
|
||||
const formatDate = dateString => {
|
||||
const date = dayjs(dateString);
|
||||
return date.isToday() ? date.format('HH:mm') : date.format('YYYY-MM-DD');
|
||||
const date = dayjs(dateString);
|
||||
return date.isToday() ? date.format('HH:mm') : date.format('YYYY-MM-DD');
|
||||
};
|
||||
|
||||
// 제목이 14글자 넘어가면 ... 처리하는 함수
|
||||
const truncateTitle = title => {
|
||||
return title.length > 7 ? title.slice(0, 7) + '...' : title;
|
||||
return title.length > 7 ? title.slice(0, 7) + '...' : title;
|
||||
};
|
||||
|
||||
// 공지사항 데이터 로드 (최대 5개)
|
||||
const fetchNoticePosts = async () => {
|
||||
try {
|
||||
const { data } = await axios.get('board/notices', { params: { size: 8 } });
|
||||
if (data?.data) {
|
||||
noticeList.value = data.data.map(post => ({
|
||||
id: post.id,
|
||||
title: post.title,
|
||||
date: formatDate(post.date),
|
||||
rawDate: post.date,
|
||||
views: post.cnt || 0,
|
||||
commentCount: post.commentCount,
|
||||
img: post.firstImageUrl,
|
||||
author: post.author || '관리자',
|
||||
nickname: post.nickname || '관리자',
|
||||
hasAttachment: post.hasAttachment, // 첨부파일 유무
|
||||
}));
|
||||
try {
|
||||
const { data } = await axios.get('board/notices', { params: { size: 8 } });
|
||||
if (data?.data) {
|
||||
noticeList.value = data.data.map(post => ({
|
||||
id: post.id,
|
||||
title: post.title,
|
||||
date: formatDate(post.date),
|
||||
rawDate: post.date,
|
||||
views: post.cnt || 0,
|
||||
commentCount: post.commentCount,
|
||||
img: post.firstImageUrl,
|
||||
author: post.author || '관리자',
|
||||
nickname: post.nickname || '관리자',
|
||||
hasAttachment: post.hasAttachment, // 첨부파일 유무
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
};
|
||||
|
||||
// board/general 게시글 로드 후 자유게시판과 익명게시판으로 분리 (최대 10개 조회 → 각각 최대 5개)
|
||||
const fetchGeneralPosts = async () => {
|
||||
try {
|
||||
const { data } = await axios.get('board/general', { params: { size: 16 } });
|
||||
if (data?.data && data.data.list) {
|
||||
const freePosts = [];
|
||||
const anonymousPosts = [];
|
||||
data.data.list.forEach(post => {
|
||||
if (post.nickname) {
|
||||
// 닉네임이 있으면 익명게시판 데이터
|
||||
anonymousPosts.push({
|
||||
id: post.id,
|
||||
title: post.title,
|
||||
date: formatDate(post.date),
|
||||
img: post.firstImageUrl,
|
||||
rawDate: post.date,
|
||||
views: post.cnt || 0,
|
||||
commentCount: post.commentCount,
|
||||
nickname: post.nickname,
|
||||
hasAttachment: post.hasAttachment, // 첨부파일 유무
|
||||
});
|
||||
} else {
|
||||
// 닉네임이 없으면 자유게시판 데이터
|
||||
freePosts.push({
|
||||
id: post.id,
|
||||
title: post.title,
|
||||
date: formatDate(post.date),
|
||||
rawDate: post.date,
|
||||
views: post.cnt || 0,
|
||||
img: post.firstImageUrl,
|
||||
commentCount: post.commentCount,
|
||||
author: post.author || '익명',
|
||||
hasAttachment: post.hasAttachment, // 첨부파일 유무
|
||||
try {
|
||||
const { data } = await axios.get('board/general', { params: { size: 16 } });
|
||||
if (data?.data && data.data.list) {
|
||||
const freePosts = [];
|
||||
const anonymousPosts = [];
|
||||
data.data.list.forEach(post => {
|
||||
if (post.nickname) {
|
||||
// 닉네임이 있으면 익명게시판 데이터
|
||||
anonymousPosts.push({
|
||||
id: post.id,
|
||||
title: post.title,
|
||||
date: formatDate(post.date),
|
||||
img: post.firstImageUrl,
|
||||
rawDate: post.date,
|
||||
views: post.cnt || 0,
|
||||
commentCount: post.commentCount,
|
||||
nickname: post.nickname,
|
||||
hasAttachment: post.hasAttachment, // 첨부파일 유무
|
||||
});
|
||||
} else {
|
||||
// 닉네임이 없으면 자유게시판 데이터
|
||||
freePosts.push({
|
||||
id: post.id,
|
||||
title: post.title,
|
||||
date: formatDate(post.date),
|
||||
rawDate: post.date,
|
||||
views: post.cnt || 0,
|
||||
img: post.firstImageUrl,
|
||||
commentCount: post.commentCount,
|
||||
author: post.author || '익명',
|
||||
hasAttachment: post.hasAttachment, // 첨부파일 유무
|
||||
});
|
||||
}
|
||||
});
|
||||
freeList.value = freePosts.slice(0, 8);
|
||||
anonymousList.value = anonymousPosts.slice(0, 8);
|
||||
}
|
||||
});
|
||||
freeList.value = freePosts.slice(0, 8);
|
||||
anonymousList.value = anonymousPosts.slice(0, 8);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
// 탭 변경 함수
|
||||
const changeBoard = type => {
|
||||
selectedBoard.value = type;
|
||||
selectedBoard.value = type;
|
||||
};
|
||||
|
||||
// 상세 페이지 이동 (게시판 타입 전달)
|
||||
const goDetail = (id, boardType) => {
|
||||
router.push({ name: 'BoardDetail', params: { id }, query: { type: boardType } });
|
||||
router.push({ name: 'BoardDetail', params: { id }, query: { type: boardType } });
|
||||
};
|
||||
|
||||
// 모달이 열릴 때 데이터 로드
|
||||
@ -220,6 +228,6 @@ fetchGeneralPosts();
|
||||
|
||||
<style scoped>
|
||||
.table > :not(caption) > * > * {
|
||||
padding: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -80,7 +80,7 @@
|
||||
<span v-if="isNewPost(notice.rawDate)" class="box-new badge text-white ms-2 fs-tiny"> N </span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center">{{ notice.author }}</td>
|
||||
<td class="text-start">{{ notice.author }}</td>
|
||||
<td class="text-center">{{ notice.date }}</td>
|
||||
<td class="text-center">{{ notice.views }}</td>
|
||||
</tr>
|
||||
@ -105,7 +105,7 @@
|
||||
<span v-if="isNewPost(post.rawDate)" class="box-new badge text-white ms-2 fs-tiny">N</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center">{{ post.nickname ? post.nickname : post.author }}</td>
|
||||
<td class="text-start nickname-ellipsis">{{ post.nickname ? post.nickname : post.author }}</td>
|
||||
<td class="text-center">{{ post.date }}</td>
|
||||
<td class="text-center">{{ post.views }}</td>
|
||||
</tr>
|
||||
@ -384,4 +384,5 @@
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@ -50,6 +50,8 @@
|
||||
v-model="nickname"
|
||||
@update:alert="nicknameAlert = $event"
|
||||
@input="validateNickname"
|
||||
@keypress="noSpace"
|
||||
:maxlength="6"
|
||||
/>
|
||||
<FormInput
|
||||
title="비밀번호"
|
||||
@ -146,6 +148,10 @@
|
||||
const editorUploadedImgList = ref([]);
|
||||
const editorDeleteImgList = ref([]);
|
||||
|
||||
const noSpace = (e) => {
|
||||
if (e.key === ' ') e.preventDefault();
|
||||
};
|
||||
|
||||
const fetchCategories = async () => {
|
||||
const response = await axios.get('board/categories');
|
||||
categoryList.value = response.data.data;
|
||||
@ -206,7 +212,8 @@
|
||||
|
||||
const validateNickname = () => {
|
||||
if (categoryValue.value === 300102) {
|
||||
nicknameAlert.value = nickname.value.trim().length === 0;
|
||||
nickname.value = nickname.value.replace(/\s/g, ''); // 공백 제거
|
||||
nicknameAlert.value = nickname.value.length === 0 ;
|
||||
} else {
|
||||
nicknameAlert.value = false;
|
||||
}
|
||||
|
||||
@ -55,8 +55,11 @@
|
||||
<ArrInput title="주소" name="address" v-model="form.address" :disabled="true" />
|
||||
|
||||
<UserFormInput title="전화번호" name="phone" :value="form.phone"
|
||||
@update:data="form.phone = $event" @blur="checkPhoneDuplicate"
|
||||
@update:data="form.phone = $event" @blur="checkPhoneDuplicateAndFormat"
|
||||
:maxlength="11" @keypress="onlyNumber" />
|
||||
<span v-if="phoneFormatError" class="text-danger invalid-feedback mt-1 d-block">
|
||||
전화번호 형식이 올바르지 않습니다.
|
||||
</span>
|
||||
<span v-if="phoneDuplicated" class="text-danger invalid-feedback mt-1 d-block">
|
||||
이미 사용 중인 전화번호입니다.
|
||||
</span>
|
||||
@ -142,6 +145,7 @@ const colorList = ref([]);
|
||||
|
||||
const password = ref({ current: '', new: '', confirm: '' });
|
||||
const passwordError = ref(false);
|
||||
const phoneFormatError = ref(false);
|
||||
const showResetPw = ref(false);
|
||||
|
||||
const canResetPassword = computed(() => {
|
||||
@ -162,93 +166,101 @@ watch(
|
||||
);
|
||||
|
||||
const isChanged = computed(() => {
|
||||
const f = form.value;
|
||||
const o = originalData.value;
|
||||
return (
|
||||
f.entryDate !== o.entryDate || f.birth !== o.birth || f.phone !== o.phone ||
|
||||
f.color !== o.color || f.mbti !== o.mbti || profileChanged.value ||
|
||||
f.address.address !== o.address.address ||
|
||||
f.address.detailAddress !== o.address.detailAddress ||
|
||||
f.address.postcode !== o.address.postcode
|
||||
);
|
||||
const f = form.value;
|
||||
const o = originalData.value;
|
||||
return (
|
||||
f.entryDate !== o.entryDate || f.birth !== o.birth || f.phone !== o.phone ||
|
||||
f.color !== o.color || f.mbti !== o.mbti || profileChanged.value ||
|
||||
f.address.address !== o.address.address ||
|
||||
f.address.detailAddress !== o.address.detailAddress ||
|
||||
f.address.postcode !== o.address.postcode
|
||||
);
|
||||
});
|
||||
|
||||
const baseUrl = $api.defaults.baseURL.replace(/api\/$/, '');
|
||||
const defaultProfile = "img/avatars/default-Profile.jpg";
|
||||
const getProfileImageUrl = (fileName) =>
|
||||
fileName?.trim() ? `${baseUrl}upload/img/profile/${fileName}?t=${Date.now()}` : defaultProfile;
|
||||
|
||||
const defaultProfile = "/img/icons/icon.png";
|
||||
const getProfileImageUrl = (profilePath) =>
|
||||
profilePath && profilePath.trim() ? `${baseUrl}upload/img/profile/${profilePath}` : defaultProfile;
|
||||
const profilePreviewStyle = computed(() => ({
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
backgroundImage: `url(${profile.value})`,
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center'
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
backgroundImage: `url(${profile.value})`,
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center'
|
||||
}));
|
||||
|
||||
const profileUpload = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
if (file.size > 5 * 1024 * 1024 || !['image/jpeg', 'image/png'].includes(file.type)) {
|
||||
profilerr.value = '5MB 이하의 JPG/PNG 파일만 업로드 가능합니다.';
|
||||
return;
|
||||
}
|
||||
profilerr.value = '';
|
||||
if (currentBlobUrl.value) URL.revokeObjectURL(currentBlobUrl.value);
|
||||
uploadedFile.value = file;
|
||||
const newBlobUrl = URL.createObjectURL(file);
|
||||
profile.value = newBlobUrl;
|
||||
currentBlobUrl.value = newBlobUrl;
|
||||
profileChanged.value = true;
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
if (file.size > 5 * 1024 * 1024 || !['image/jpeg', 'image/png'].includes(file.type)) {
|
||||
profilerr.value = '5MB 이하의 JPG/PNG 파일만 업로드 가능합니다.';
|
||||
return;
|
||||
}
|
||||
profilerr.value = '';
|
||||
if (currentBlobUrl.value) URL.revokeObjectURL(currentBlobUrl.value);
|
||||
uploadedFile.value = file;
|
||||
const newBlobUrl = URL.createObjectURL(file);
|
||||
profile.value = newBlobUrl;
|
||||
currentBlobUrl.value = newBlobUrl;
|
||||
profileChanged.value = true;
|
||||
};
|
||||
|
||||
const onlyNumber = (e) => {
|
||||
if (!/[0-9]/.test(e.key)) e.preventDefault();
|
||||
if (!/[0-9]/.test(e.key)) e.preventDefault();
|
||||
};
|
||||
|
||||
const checkPhoneDuplicate = async () => {
|
||||
const currentPhone = form.value.phone;
|
||||
phoneDuplicated.value = currentPhone !== originalData.value.phone &&
|
||||
!(await $api.get('/user/checkPhone', { params: { memberTel: currentPhone } })).data.data;
|
||||
const checkPhoneDuplicateAndFormat = async () => {
|
||||
const phone = form.value.phone.trim();
|
||||
|
||||
// 전화번호 정규식
|
||||
const phoneRegex = /^010\d{8}$/;
|
||||
phoneFormatError.value = !phoneRegex.test(phone);
|
||||
|
||||
if (!phoneFormatError.value) {
|
||||
phoneDuplicated.value = phone !== originalData.value.phone &&
|
||||
!(await $api.get('/user/checkPhone', {
|
||||
params: { memberTel: currentPhone },
|
||||
})).data.data;
|
||||
}
|
||||
};
|
||||
|
||||
const handleColorUpdate = async (colorVal) => {
|
||||
form.value.color = colorVal;
|
||||
colorDuplicated.value = colorVal !== originalData.value.color &&
|
||||
(await $api.get('/user/checkColor', { params: { memberCol: colorVal } })).data.data;
|
||||
form.value.color = colorVal;
|
||||
colorDuplicated.value = colorVal !== originalData.value.color &&
|
||||
(await $api.get('/user/checkColor', { params: { memberCol: colorVal } })).data.data;
|
||||
};
|
||||
|
||||
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;
|
||||
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 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 user = (await $api.get('/user/userInfo')).data.data;
|
||||
const serverColors = (await $api.get('/user/color', { params: { type: 'YON' } })).data.data.map(c => ({
|
||||
value: c.CMNCODVAL, label: c.CMNCODNAM
|
||||
const user = (await $api.get('/user/userInfo')).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;
|
||||
@ -275,32 +287,32 @@ 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 }));
|
||||
mbtiList.value = mbtiRes.data.data.map(m => ({ value: m.CMNCODVAL, label: m.CMNCODNAM }));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const formData = new FormData();
|
||||
Object.entries(form.value).forEach(([k, v]) => {
|
||||
if (typeof v === 'object') {
|
||||
formData.append('address', v.address);
|
||||
formData.append('detailAddress', v.detailAddress);
|
||||
formData.append('postcode', v.postcode);
|
||||
} else {
|
||||
formData.append(k, v);
|
||||
const formData = new FormData();
|
||||
Object.entries(form.value).forEach(([k, v]) => {
|
||||
if (typeof v === 'object') {
|
||||
formData.append('address', v.address);
|
||||
formData.append('detailAddress', v.detailAddress);
|
||||
formData.append('postcode', v.postcode);
|
||||
} else {
|
||||
formData.append(k, v);
|
||||
}
|
||||
});
|
||||
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);
|
||||
|
||||
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' });
|
||||
}
|
||||
|
||||
await $api.patch('/user/updateInfo', formData, { isFormData: true });
|
||||
originalData.value = { ...form.value };
|
||||
profileChanged.value = false;
|
||||
location.reload();
|
||||
toastStore.onToast('정보가 수정되었습니다.', 's');
|
||||
await $api.patch('/user/updateInfo', formData, { isFormData: true });
|
||||
originalData.value = { ...form.value };
|
||||
profileChanged.value = false;
|
||||
location.reload();
|
||||
toastStore.onToast('정보가 수정되었습니다.', 's');
|
||||
};
|
||||
|
||||
onMounted(() => loadInitialData());
|
||||
|
||||
Loading…
Reference in New Issue
Block a user