From 68d96fa0987e92d12ca0bd06946c89e3c83bb443 Mon Sep 17 00:00:00 2001 From: yoon Date: Mon, 24 Feb 2025 15:44:01 +0900 Subject: [PATCH 1/6] =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EB=AA=85=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/commuters/{Calendar.vue => CommuteCalendar.vue} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/commuters/{Calendar.vue => CommuteCalendar.vue} (100%) diff --git a/src/components/commuters/Calendar.vue b/src/components/commuters/CommuteCalendar.vue similarity index 100% rename from src/components/commuters/Calendar.vue rename to src/components/commuters/CommuteCalendar.vue From fc443dacdbb96fecceb78baafe7d62c5b4e3cde7 Mon Sep 17 00:00:00 2001 From: yoon Date: Tue, 25 Feb 2025 14:18:20 +0900 Subject: [PATCH 2/6] =?UTF-8?q?=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20stor?= =?UTF-8?q?e=EB=A1=9C=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/projectlist/ProjectList.vue | 40 +++++++++++++--------- src/stores/useProjectStore.js | 30 ++++++++++++++++ 2 files changed, 54 insertions(+), 16 deletions(-) create mode 100644 src/stores/useProjectStore.js diff --git a/src/components/projectlist/ProjectList.vue b/src/components/projectlist/ProjectList.vue index 9157c79..0a8f549 100644 --- a/src/components/projectlist/ProjectList.vue +++ b/src/components/projectlist/ProjectList.vue @@ -7,11 +7,11 @@
-
+

등록된 프로젝트가 없습니다.

-
+
{ - const res = await $api.get('project/select', { - params: { - searchKeyword : searchText.value, - category : selectedYear.value, - }, - }); - projectList.value = res.data.data.projectList; -}; - // 검색 처리 const search = async (searchKeyword) => { searchText.value = searchKeyword.trim(); @@ -237,6 +227,10 @@ const selectedYear = computed(() => { return yearCategory.value.find(item => item.value === selectedCategory.value)?.label || null; }); +// 프로젝트 목록 조회 +const getProjectList = async () => { + await projectStore.getProjectList(searchText.value, selectedYear.value); +}; // 카테고리 변경 감지 watch(selectedCategory, async () => { @@ -250,6 +244,20 @@ const openCreateModal = () => { const closeCreateModal = () => { isCreateModalOpen.value = false; + resetCreateForm(); +}; + +const resetCreateForm = () => { + name.value = ''; + color.value = ''; + address.value = ''; + detailAddress.value = ''; + postcode.value = ''; + startDay.value = today; + endDay.value = ''; + description.value = ''; + nameAlert.value = false; + addressAlert.value = false; }; // 등록 :: 주소 업데이트 핸들러 @@ -283,7 +291,7 @@ const handleCreate = async () => { if (res.status === 200) { toastStore.onToast('프로젝트가 등록되었습니다.', 's'); closeCreateModal(); - location.reload(); + getProjectList(); } }); }; @@ -307,7 +315,7 @@ const allColors = computed(() => { // 변경된 내용 있는지 확인 const hasChanges = computed(() => { - const original = projectList.value.find(p => p.PROJCTSEQ === selectedProject.value.PROJCTSEQ); + const original = projectStore.projectList.find(p => p.PROJCTSEQ === selectedProject.value.PROJCTSEQ); if (!original) return false; return ( diff --git a/src/stores/useProjectStore.js b/src/stores/useProjectStore.js new file mode 100644 index 0000000..46d8b7a --- /dev/null +++ b/src/stores/useProjectStore.js @@ -0,0 +1,30 @@ +/* + 작성자 : 박지윤 + 작성일 : 2025-02-25 + 수정자 : + 수정일 : + 설명 : 프로젝트 목록 +*/ +import { defineStore } from 'pinia'; +import { ref } from 'vue'; +import $api from '@api'; + +export const useProjectStore = defineStore('project', () => { + const projectList = ref([]); + + const getProjectList = async (searchText, selectedYear) => { + try { + const res = await $api.get('project/select', { + params: { + searchKeyword: searchText, + category: selectedYear, + }, + }); + projectList.value = res.data.data.projectList; + } catch (error) { + console.error('프로젝트 목록 조회 실패:', error); + } + }; + + return { projectList, getProjectList }; +}); From 8ffb483aa1e3fdc974f8b33858c5604f89aa43f3 Mon Sep 17 00:00:00 2001 From: yoon Date: Tue, 25 Feb 2025 14:18:48 +0900 Subject: [PATCH 3/6] =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EB=AA=85=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/commuters/TheCommuters.vue | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/views/commuters/TheCommuters.vue b/src/views/commuters/TheCommuters.vue index 545eca1..4690967 100644 --- a/src/views/commuters/TheCommuters.vue +++ b/src/views/commuters/TheCommuters.vue @@ -1,9 +1,7 @@ From 6ecf2d5aa15c33ab3ac41b725ae55e03011ba99c Mon Sep 17 00:00:00 2001 From: yoon Date: Tue, 25 Feb 2025 14:18:56 +0900 Subject: [PATCH 4/6] =?UTF-8?q?=EB=AF=B8=EC=99=84=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/commuters/CommuteCalendar.vue | 113 +++++++++++++------ 1 file changed, 79 insertions(+), 34 deletions(-) diff --git a/src/components/commuters/CommuteCalendar.vue b/src/components/commuters/CommuteCalendar.vue index 08af625..9898eaa 100644 --- a/src/components/commuters/CommuteCalendar.vue +++ b/src/components/commuters/CommuteCalendar.vue @@ -2,9 +2,32 @@
-
+
- Profile Image + Profile Image +

+ {{ user.name }} +

+ +
+
+

출근시간

+ +
+ +
+

퇴근시간

+ +
+ +
+ {{ post.PROJCTNAM }} +
+
@@ -62,15 +85,16 @@ import { isEmpty } from '@/common/utils'; import FormInput from '../input/FormInput.vue'; import 'flatpickr/dist/flatpickr.min.css'; import '@/assets/css/app-calendar.css'; -import { useThemeStore } from '@s/darkmode'; import { fetchHolidays } from '@c/calendar/holiday'; import { useUserInfoStore } from '@/stores/useUserInfoStore'; +import { useProjectStore } from '@/stores/useProjectStore'; -const user = ref(null); - +const baseUrl = $api.defaults.baseURL.replace(/api\/$/, ''); +const user = ref({}); +const project = ref({}); const userStore = useUserInfoStore(); +const projectStore = useProjectStore(); -const themeStore = useThemeStore(); const dayjs = inject('dayjs'); const fullCalendarRef = ref(null); const calendarEvents = ref([]); @@ -85,118 +109,139 @@ const selectedDate = ref(null); // 날짜 선택 핸들러 const handleDateSelect = (selectedDates) => { if (selectedDates.length > 0) { + // 선택된 첫 번째 날짜를 YYYY-MM-DD 형식으로 변환 const selectedDate = dayjs(selectedDates[0]).format('YYYY-MM-DD'); eventDate.value = selectedDate; - showModal(); + showModal(); // 모달 표시 } }; -// 달력 데이터 가져오기 +// 캘린더 데이터 가져오기 const fetchData = async () => { + // FullCalendar API 인스턴스 가져오기 const calendarApi = fullCalendarRef.value?.getApi(); if (!calendarApi) return; + // 현재 표시된 달력의 연도, 월 추출 const date = calendarApi.currentData.viewTitle; const dateArr = date.split(' '); let currentYear = dateArr[0].trim(); let currentMonth = dateArr[1].trim(); const regex = /\D/g; + // 숫자가 아닌 문자 제거 후 정수로 변환 currentYear = parseInt(currentYear.replace(regex, ''), 10); currentMonth = parseInt(currentMonth.replace(regex, ''), 10); try { + // 현재 표시 중인 월의 공휴일 정보 가져오기 const holidayEvents = await fetchHolidays(currentYear, String(currentMonth).padStart(2, '0')); + // 기존 이벤트에서 공휴일 이벤트를 제외한 이벤트만 필터링 const existingEvents = calendarEvents.value.filter(event => !event.classNames?.includes('holiday-event')); + // 필터링된 이벤트와 새로 가져온 공휴일 이벤트 병합 calendarEvents.value = [...existingEvents, ...holidayEvents]; } catch (error) { console.error('공휴일 정보 로딩 실패:', error); } }; -// 달력 이동 +// 캘린더 이동 함수 (이전, 다음, 오늘) const moveCalendar = async (value = 0) => { const calendarApi = fullCalendarRef.value?.getApi(); if (value === 1) { - calendarApi.prev(); + calendarApi.prev(); // 이전 달로 이동 } else if (value === 2) { - calendarApi.next(); + calendarApi.next(); // 다음 달로 이동 } else if (value === 3) { - calendarApi.today(); + calendarApi.today(); // 오늘 날짜로 이동 } + // 캘린더 이동 후 데이터 다시 가져오기 await fetchData(); }; +// 모달 표시 함수 const showModal = () => { isModalVisible.value = true; }; +// 모달 닫기 함수 const closeModal = () => { isModalVisible.value = false; + // 입력 필드 초기화 eventTitle.value = ''; eventDate.value = ''; }; +// 이벤트 추가 함수 const addEvent = () => { + // 이벤트 유효성 검사 if (!checkEvent()) { + // 유효성 검사 통과 시 이벤트 추가 calendarEvents.value.push({ title: eventTitle.value, start: eventDate.value, backgroundColor: '#4CAF50' // 일반 이벤트 색상 }); - closeModal(); + closeModal(); // 모달 닫기 } }; +// 이벤트 유효성 검사 함수 const checkEvent = () => { + // 제목과 날짜가 비어있는지 확인 eventAlert.value = isEmpty(eventTitle.value); eventDateAlert.value = isEmpty(eventDate.value); + // 하나라도 비어있으면 true 반환 (유효성 검사 실패) return eventAlert.value || eventDateAlert.value; }; +// 캘린더 옵션 설정 const calendarOptions = reactive({ - plugins: [dayGridPlugin, interactionPlugin], - initialView: 'dayGridMonth', - headerToolbar: { - left: 'today', - center: 'title', - right: 'prev,next', + plugins: [dayGridPlugin, interactionPlugin], // 사용할 플러그인 + initialView: 'dayGridMonth', // 초기 뷰 (월간) + headerToolbar: { // 상단 툴바 구성 + left: 'today', // 왼쪽: 오늘 버튼 + center: 'title', // 중앙: 제목(연월) + right: 'prev,next', // 오른쪽: 이전/다음 버튼 }, - locale: 'kr', - events: calendarEvents, - eventOrder: 'sortIdx', - selectable: true, - dateClick: handleDateSelect, - droppable: false, - eventDisplay: 'block', + locale: 'kr', // 한국어 지역화 + events: calendarEvents, // 표시할 이벤트 데이터 + eventOrder: 'sortIdx', // 이벤트 정렬 기준 + selectable: true, // 날짜 선택 가능 여부 + dateClick: handleDateSelect, // 날짜 클릭 이벤트 핸들러 + droppable: false, // 드래그 앤 드롭 비활성화 + eventDisplay: 'block', // 이벤트 표시 방식 + // 커스텀 버튼 정의 customButtons: { prev: { - text: 'PREV', - click: () => moveCalendar(1), + text: 'PREV', // 이전 버튼 텍스트 + click: () => moveCalendar(1), // 클릭 시 이전 달로 이동 }, today: { - text: 'TODAY', - click: () => moveCalendar(3), + text: 'TODAY', // 오늘 버튼 텍스트 + click: () => moveCalendar(3), // 클릭 시 오늘로 이동 }, next: { - text: 'NEXT', - click: () => moveCalendar(2), + text: 'NEXT', // 다음 버튼 텍스트 + click: () => moveCalendar(2), // 클릭 시 다음 달로 이동 }, }, }); -// 달력 뷰 변경 감지 +// 달력 뷰 변경 감지 (월 변경 시 데이터 다시 가져오기) watch(() => fullCalendarRef.value?.getApi().currentData.viewTitle, async () => { await fetchData(); }); - +console.log(project) onMounted(async () => { await fetchData(); await userStore.userInfo(); user.value = userStore.user; + await projectStore.getProjectList(); + project.value = projectStore.projectList; }); From 37c335fc12fe4185d185e1082a36d84642b9fec8 Mon Sep 17 00:00:00 2001 From: yoon Date: Tue, 25 Feb 2025 16:00:45 +0900 Subject: [PATCH 5/6] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=8B=9C=20?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=ED=8E=98=EC=9D=B4=EC=A7=80=EB=A1=9C=20?= =?UTF-8?q?=EA=B0=80=EC=A7=80=EB=8A=94=EB=8D=B0=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=EC=9D=80=20=EC=95=88=EB=B0=94=EB=80=8C=EB=8A=94=EA=B1=B0=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/user/LoginForm.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/user/LoginForm.vue b/src/components/user/LoginForm.vue index caea1af..32e7999 100644 --- a/src/components/user/LoginForm.vue +++ b/src/components/user/LoginForm.vue @@ -32,7 +32,7 @@ diff --git a/src/components/board/BoardCommentArea.vue b/src/components/board/BoardCommentArea.vue index 76b93b0..11d1d40 100644 --- a/src/components/board/BoardCommentArea.vue +++ b/src/components/board/BoardCommentArea.vue @@ -17,6 +17,7 @@ rows="3" v-model="comment" > + {{ commentAlert }}
@@ -42,6 +43,7 @@ id="basic-default-password" class="form-control flex-grow-1" v-model="password" + placeholder="비밀번호 입력" /> {{ passwordAlert }}
@@ -74,28 +76,35 @@ const props = defineProps({ passwordAlert: { type: String, default: false + }, + commentAlert: { + type: String, + default: false } }); + const comment = ref(''); const password = ref(''); -const isCheck = ref(false); -const emit = defineEmits(['submitComment']); +const isCheck = ref(props.unknown); +const emit = defineEmits(['submitComment']); +const LOCBRDTYP = isCheck.value ? '300102' : null; function handleCommentSubmit() { - if (props.unknown && isCheck.value && !password.value) { - alert('익명 댓글을 작성하려면 비밀번호를 입력해야 합니다.'); - return; - } - const LOCBRDTYP = isCheck.value ? '300102' : null; emit('submitComment', { comment: comment.value, password: isCheck.value ? password.value : '', - LOCBRDTYP, + isCheck: isCheck.value, + LOCBRDTYP, // 익명일 경우 '300102' 설정 isCheck: isCheck.value }); - - comment.value = ''; - password.value = ''; - isCheck.value = false; // 초기화 } + +watch(() => props.passwordAlert, () => { + if (!props.passwordAlert) { + comment.value = ''; + password.value = ''; + } +}); + + diff --git a/src/views/board/BoardView.vue b/src/views/board/BoardView.vue index 8514b98..3662544 100644 --- a/src/views/board/BoardView.vue +++ b/src/views/board/BoardView.vue @@ -140,7 +140,6 @@ import { ref, onMounted, computed } from 'vue'; import { useRoute, useRouter } from 'vue-router'; import { useUserInfoStore } from '@/stores/useUserInfoStore'; import axios from '@api'; -import { formattedDate } from '@/common/formattedDate.js'; // 게시물 데이터 상태 const profileName = ref(''); @@ -376,63 +375,77 @@ const fetchComments = async (page = 1) => { }; // 댓글 작성 -const handleCommentSubmit = async (data) => { +const handleCommentSubmit = async (data, isCheck) => { + if (!data) { + console.error("handleCommentSubmit: data가 undefined입니다."); + return; + } + const { comment, password } = data; - const LOCBRDTYP = data.LOCBRDTYP || null; // undefined 방지 + const LOCBRDTYP = data.LOCBRDTYP || null; + + if (!comment || comment.trim() === "") { + commentAlert.value = '댓글을 입력해주세요.'; + return; + } else { + commentAlert.value = ''; + } + + if (unknown.value && isCheck && (!password || password.trim() === "")) { + passwordAlert.value = "비밀번호를 입력해야 합니다."; + return; + } try { const response = await axios.post(`board/${currentBoardId.value}/comment`, { LOCBRDSEQ: currentBoardId.value, LOCCMTRPY: comment, - LOCCMTPWD: password, + LOCCMTPWD: isCheck ? password : '', LOCCMTPNT: 1, - LOCBRDTYP // 익명 여부 전달 + LOCBRDTYP }); if (response.status === 200) { console.log('댓글 작성 성공:', response.data.message); + passwordAlert.value = ''; + commentAlert.value = ''; await fetchComments(); } else { - console.log('댓글 작성 실패:', response.data.message); + console.error('댓글 작성 실패:', response.data.message); } } catch (error) { - console.log('댓글 작성 중 오류 발생:', error); + console.error('댓글 작성 중 오류 발생:', error); } }; - // 대댓글 추가 (부모 `BoardCommentList`로부터 이벤트 받아서 처리) const handleCommentReply = async (reply) => { try { - // 익명 여부 체크 (체크박스가 체크되었을 경우 LOCBRDTYP을 300102로 설정) - - const requestBody = { + const response = await axios.post(`board/${currentBoardId.value}/comment`, { LOCBRDSEQ: currentBoardId.value, LOCCMTRPY: reply.comment, LOCCMTPWD: reply.password || null, LOCCMTPNT: reply.parentId, LOCBRDTYP: reply.isCheck ? "300102" : null - }; - console.log(requestBody) - const response = await axios.post(`board/${currentBoardId.value}/comment`, requestBody); + }); if (response.status === 200) { - if (response.data.code === 200) { - console.log('✅ 대댓글 작성 성공:', response.data); - await fetchComments(); + if (response.data.code === 200) { // 서버 응답 코드도 확인 + console.log('대댓글 작성 성공:', response.data); + await fetchComments(); // 댓글 목록 새로고침 } else { - console.log('❌ 대댓글 작성 실패 - 서버 응답:', response.data); + console.log('대댓글 작성 실패 - 서버 응답:', response.data); alert('대댓글 작성에 실패했습니다.'); } } } catch (error) { - console.error('🚨 대댓글 작성 중 오류 발생:', error); + console.error('대댓글 작성 중 오류 발생:', error); if (error.response) { - console.error('📌 서버 응답 에러:', error.response.data); + console.error('서버 응답 에러:', error.response.data); } - alert('❌ 대댓글 작성 중 오류가 발생했습니다.'); + alert('대댓글 작성 중 오류가 발생했습니다.'); } -}; +} // 게시글 수정 버튼 클릭 const editClick = (unknown) => { @@ -484,6 +497,7 @@ const editComment = (comment) => { // 익명일 경우 비밀번호 입력창 활성화 if (unknown.value) { + console.log('익명 코멘트인가?') toggleCommentPassword(comment, "edit"); } } @@ -736,6 +750,14 @@ const handleCommentDeleted = (deletedCommentId) => { console.error("❌ 삭제할 댓글을 찾을 수 없음:", deletedCommentId); }; + +// 날짜 +const formattedDate = (dateString) => { + if (!dateString) return "날짜 없음"; + const dateObj = new Date(dateString); + return `${dateObj.getFullYear()}-${String(dateObj.getMonth() + 1).padStart(2, '0')}-${String(dateObj.getDate()).padStart(2, '0')} ${String(dateObj.getHours()).padStart(2, '0')}:${String(dateObj.getMinutes()).padStart(2, '0')}`; +}; + const formattedBoardDate = computed(() => formattedDate(date.value)); // 컴포넌트 마운트 시 데이터 로드