Merge branch 'main' of http://192.168.0.251:3000/localnet/localhost-front
This commit is contained in:
commit
98aa44f497
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<BoardProfile
|
<BoardProfile
|
||||||
:unknown="unknown"
|
:unknown="comment.author === '익명'"
|
||||||
:isCommentAuthor="isCommentAuthor"
|
:isCommentAuthor="isCommentAuthor"
|
||||||
:boardId="comment.boardId"
|
:boardId="comment.boardId"
|
||||||
:profileName="comment.author"
|
:profileName="comment.author"
|
||||||
@ -11,7 +11,7 @@
|
|||||||
:isLike="!isLike"
|
:isLike="!isLike"
|
||||||
:isCommentPassword="comment.isCommentPassword"
|
:isCommentPassword="comment.isCommentPassword"
|
||||||
:isCommentProfile="true"
|
:isCommentProfile="true"
|
||||||
@editClick="$emit('editClick', comment)"
|
@editClick="aaaa"
|
||||||
@deleteClick="$emit('deleteClick', comment)"
|
@deleteClick="$emit('deleteClick', comment)"
|
||||||
@updateReaction="handleUpdateReaction"
|
@updateReaction="handleUpdateReaction"
|
||||||
/>
|
/>
|
||||||
@ -29,8 +29,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<span v-if="passwordCommentAlert" class="invalid-feedback d-block text-start">{{ passwordCommentAlert }}</span>
|
<span v-if="passwordCommentAlert" class="invalid-feedback d-block text-start">{{ passwordCommentAlert }}</span>
|
||||||
</div>
|
</div>
|
||||||
<p>authorId:{{ comment.authorId }}</p>
|
<!-- <p>authorId:{{ comment.authorId }}</p>
|
||||||
<p>코멘트 비교: {{comment.isCommentAuthor}}</p>
|
<p>코멘트 비교: {{comment.isCommentAuthor}}</p> -->
|
||||||
|
|
||||||
|
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
@ -47,7 +47,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PlusButton v-if="isPlusButton" @click="toggleComment" class="mt-6"/>
|
<PlusButton v-if="isPlusButton" @click="toggleComment" class="mt-6"/>
|
||||||
<BoardCommentArea v-if="isComment" @submitComment="submitComment"/>
|
<BoardCommentArea v-if="isComment" :unknown="unknown" @submitComment="submitComment"/>
|
||||||
|
|
||||||
<!-- 대댓글 -->
|
<!-- 대댓글 -->
|
||||||
<ul v-if="comment.children && comment.children.length" class="list-unstyled">
|
<ul v-if="comment.children && comment.children.length" class="list-unstyled">
|
||||||
@ -56,11 +56,20 @@
|
|||||||
:key="child.commentId"
|
:key="child.commentId"
|
||||||
class="mt-8 pt-6 ps-10 border-top"
|
class="mt-8 pt-6 ps-10 border-top"
|
||||||
>
|
>
|
||||||
|
<!-- <p>대댓글 데이터(JSON): {{ JSON.stringify(child, null, 2) }}</p> -->
|
||||||
|
<!-- <p>comment child: {{ comment.children }}</p> -->
|
||||||
|
<!-- :unknown="child.author === '익명'" -->
|
||||||
<BoardComment
|
<BoardComment
|
||||||
:comment="child"
|
:comment="child"
|
||||||
:unknown="unknown"
|
:unknown="child.author === '익명'"
|
||||||
:isPlusButton="false"
|
:isPlusButton="false"
|
||||||
:isLike="true"
|
:isLike="true"
|
||||||
|
:isCommentProfile="true"
|
||||||
|
:isCommentAuthor="child.isCommentAuthor"
|
||||||
|
@editClick="$emit('editClick', $event)"
|
||||||
|
@deleteClick="$emit('deleteClick', child)"
|
||||||
|
@submitEdit="(comment, editedContent) => $emit('submitEdit', comment, editedContent)"
|
||||||
|
@cancelEdit="$emit('cancelEdit', child)"
|
||||||
@submitComment="submitComment"
|
@submitComment="submitComment"
|
||||||
@updateReaction="handleUpdateReaction"
|
@updateReaction="handleUpdateReaction"
|
||||||
/>
|
/>
|
||||||
@ -155,4 +164,8 @@ const submitEdit = () => {
|
|||||||
emit('submitEdit', props.comment, localEditedContent.value);
|
emit('submitEdit', props.comment, localEditedContent.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const aaaa = () => {
|
||||||
|
emit('editClick', props.comment);
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
rows="3"
|
rows="3"
|
||||||
v-model="comment"
|
v-model="comment"
|
||||||
></textarea>
|
></textarea>
|
||||||
|
<span v-if="commentAlert" class="invalid-feedback d-block text-start ms-2">{{ commentAlert }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -42,6 +43,7 @@
|
|||||||
id="basic-default-password"
|
id="basic-default-password"
|
||||||
class="form-control flex-grow-1"
|
class="form-control flex-grow-1"
|
||||||
v-model="password"
|
v-model="password"
|
||||||
|
placeholder="비밀번호 입력"
|
||||||
/>
|
/>
|
||||||
<span v-if="passwordAlert" class="invalid-feedback d-block text-start ms-2">{{ passwordAlert }}</span>
|
<span v-if="passwordAlert" class="invalid-feedback d-block text-start ms-2">{{ passwordAlert }}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -74,28 +76,35 @@ const props = defineProps({
|
|||||||
passwordAlert: {
|
passwordAlert: {
|
||||||
type: String,
|
type: String,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
commentAlert: {
|
||||||
|
type: String,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const comment = ref('');
|
const comment = ref('');
|
||||||
const password = ref('');
|
const password = ref('');
|
||||||
const isCheck = ref(false);
|
const isCheck = ref(props.unknown);
|
||||||
const emit = defineEmits(['submitComment']);
|
|
||||||
|
|
||||||
|
const emit = defineEmits(['submitComment']);
|
||||||
|
const LOCBRDTYP = isCheck.value ? '300102' : null;
|
||||||
function handleCommentSubmit() {
|
function handleCommentSubmit() {
|
||||||
if (props.unknown && isCheck.value && !password.value) {
|
|
||||||
alert('익명 댓글을 작성하려면 비밀번호를 입력해야 합니다.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const LOCBRDTYP = isCheck.value ? '300102' : null;
|
|
||||||
emit('submitComment', {
|
emit('submitComment', {
|
||||||
comment: comment.value,
|
comment: comment.value,
|
||||||
password: isCheck.value ? password.value : '',
|
password: isCheck.value ? password.value : '',
|
||||||
LOCBRDTYP,
|
isCheck: isCheck.value,
|
||||||
|
LOCBRDTYP, // 익명일 경우 '300102' 설정
|
||||||
isCheck: isCheck.value
|
isCheck: isCheck.value
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.passwordAlert, () => {
|
||||||
|
if (!props.passwordAlert) {
|
||||||
comment.value = '';
|
comment.value = '';
|
||||||
password.value = '';
|
password.value = '';
|
||||||
isCheck.value = false; // 초기화
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -2,9 +2,32 @@
|
|||||||
<div class="container-xxl flex-grow-1 container-p-y">
|
<div class="container-xxl flex-grow-1 container-p-y">
|
||||||
<div class="card app-calendar-wrapper">
|
<div class="card app-calendar-wrapper">
|
||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="col app-calendar-sidebar border-end text-center" id="app-calendar-sidebar">
|
<div class="col-3 border-end text-center">
|
||||||
<div class="card-body pb-0">
|
<div class="card-body pb-0">
|
||||||
<img v-if="user" :src="`http://localhost:10325/upload/img/profile/${user.profile}`" alt="Profile Image" class="w-px-50 h-auto rounded-circle"/>
|
<img v-if="user" :src="`${baseUrl}upload/img/profile/${user.profile}`" alt="Profile Image" class="w-px-50 h-auto rounded-circle" @error="$event.target.src = '/img/icons/icon.png'"/>
|
||||||
|
<p class="mt-2">
|
||||||
|
{{ user.name }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="col-6 pe-1">
|
||||||
|
<p>출근시간</p>
|
||||||
|
<button class="btn btn-outline-primary border-3 w-100 py-0">
|
||||||
|
<i class='bx bx-run fs-2'></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-6 ps-1">
|
||||||
|
<p>퇴근시간</p>
|
||||||
|
<button class="btn btn-outline-secondary border-3 w-100 py-0">
|
||||||
|
<i class='bx bxs-door-open fs-2'></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-for="post in project" :key="post.PROJCTSEQ" class="border border-2 mt-3" :style="`border-color: ${post.projctcolor} !important; color: ${post.projctcolor} !important;`">
|
||||||
|
{{ post.PROJCTNAM }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -62,15 +85,16 @@ import { isEmpty } from '@/common/utils';
|
|||||||
import FormInput from '../input/FormInput.vue';
|
import FormInput from '../input/FormInput.vue';
|
||||||
import 'flatpickr/dist/flatpickr.min.css';
|
import 'flatpickr/dist/flatpickr.min.css';
|
||||||
import '@/assets/css/app-calendar.css';
|
import '@/assets/css/app-calendar.css';
|
||||||
import { useThemeStore } from '@s/darkmode';
|
|
||||||
import { fetchHolidays } from '@c/calendar/holiday';
|
import { fetchHolidays } from '@c/calendar/holiday';
|
||||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
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 userStore = useUserInfoStore();
|
||||||
|
const projectStore = useProjectStore();
|
||||||
|
|
||||||
const themeStore = useThemeStore();
|
|
||||||
const dayjs = inject('dayjs');
|
const dayjs = inject('dayjs');
|
||||||
const fullCalendarRef = ref(null);
|
const fullCalendarRef = ref(null);
|
||||||
const calendarEvents = ref([]);
|
const calendarEvents = ref([]);
|
||||||
@ -85,118 +109,139 @@ const selectedDate = ref(null);
|
|||||||
// 날짜 선택 핸들러
|
// 날짜 선택 핸들러
|
||||||
const handleDateSelect = (selectedDates) => {
|
const handleDateSelect = (selectedDates) => {
|
||||||
if (selectedDates.length > 0) {
|
if (selectedDates.length > 0) {
|
||||||
|
// 선택된 첫 번째 날짜를 YYYY-MM-DD 형식으로 변환
|
||||||
const selectedDate = dayjs(selectedDates[0]).format('YYYY-MM-DD');
|
const selectedDate = dayjs(selectedDates[0]).format('YYYY-MM-DD');
|
||||||
eventDate.value = selectedDate;
|
eventDate.value = selectedDate;
|
||||||
showModal();
|
showModal(); // 모달 표시
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 달력 데이터 가져오기
|
// 캘린더 데이터 가져오기
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
|
// FullCalendar API 인스턴스 가져오기
|
||||||
const calendarApi = fullCalendarRef.value?.getApi();
|
const calendarApi = fullCalendarRef.value?.getApi();
|
||||||
if (!calendarApi) return;
|
if (!calendarApi) return;
|
||||||
|
|
||||||
|
// 현재 표시된 달력의 연도, 월 추출
|
||||||
const date = calendarApi.currentData.viewTitle;
|
const date = calendarApi.currentData.viewTitle;
|
||||||
const dateArr = date.split(' ');
|
const dateArr = date.split(' ');
|
||||||
let currentYear = dateArr[0].trim();
|
let currentYear = dateArr[0].trim();
|
||||||
let currentMonth = dateArr[1].trim();
|
let currentMonth = dateArr[1].trim();
|
||||||
const regex = /\D/g;
|
const regex = /\D/g;
|
||||||
|
// 숫자가 아닌 문자 제거 후 정수로 변환
|
||||||
currentYear = parseInt(currentYear.replace(regex, ''), 10);
|
currentYear = parseInt(currentYear.replace(regex, ''), 10);
|
||||||
currentMonth = parseInt(currentMonth.replace(regex, ''), 10);
|
currentMonth = parseInt(currentMonth.replace(regex, ''), 10);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 현재 표시 중인 월의 공휴일 정보 가져오기
|
||||||
const holidayEvents = await fetchHolidays(currentYear, String(currentMonth).padStart(2, '0'));
|
const holidayEvents = await fetchHolidays(currentYear, String(currentMonth).padStart(2, '0'));
|
||||||
|
// 기존 이벤트에서 공휴일 이벤트를 제외한 이벤트만 필터링
|
||||||
const existingEvents = calendarEvents.value.filter(event => !event.classNames?.includes('holiday-event'));
|
const existingEvents = calendarEvents.value.filter(event => !event.classNames?.includes('holiday-event'));
|
||||||
|
// 필터링된 이벤트와 새로 가져온 공휴일 이벤트 병합
|
||||||
calendarEvents.value = [...existingEvents, ...holidayEvents];
|
calendarEvents.value = [...existingEvents, ...holidayEvents];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('공휴일 정보 로딩 실패:', error);
|
console.error('공휴일 정보 로딩 실패:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 달력 이동
|
// 캘린더 이동 함수 (이전, 다음, 오늘)
|
||||||
const moveCalendar = async (value = 0) => {
|
const moveCalendar = async (value = 0) => {
|
||||||
const calendarApi = fullCalendarRef.value?.getApi();
|
const calendarApi = fullCalendarRef.value?.getApi();
|
||||||
|
|
||||||
if (value === 1) {
|
if (value === 1) {
|
||||||
calendarApi.prev();
|
calendarApi.prev(); // 이전 달로 이동
|
||||||
} else if (value === 2) {
|
} else if (value === 2) {
|
||||||
calendarApi.next();
|
calendarApi.next(); // 다음 달로 이동
|
||||||
} else if (value === 3) {
|
} else if (value === 3) {
|
||||||
calendarApi.today();
|
calendarApi.today(); // 오늘 날짜로 이동
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 캘린더 이동 후 데이터 다시 가져오기
|
||||||
await fetchData();
|
await fetchData();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 모달 표시 함수
|
||||||
const showModal = () => {
|
const showModal = () => {
|
||||||
isModalVisible.value = true;
|
isModalVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 모달 닫기 함수
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
isModalVisible.value = false;
|
isModalVisible.value = false;
|
||||||
|
// 입력 필드 초기화
|
||||||
eventTitle.value = '';
|
eventTitle.value = '';
|
||||||
eventDate.value = '';
|
eventDate.value = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 이벤트 추가 함수
|
||||||
const addEvent = () => {
|
const addEvent = () => {
|
||||||
|
// 이벤트 유효성 검사
|
||||||
if (!checkEvent()) {
|
if (!checkEvent()) {
|
||||||
|
// 유효성 검사 통과 시 이벤트 추가
|
||||||
calendarEvents.value.push({
|
calendarEvents.value.push({
|
||||||
title: eventTitle.value,
|
title: eventTitle.value,
|
||||||
start: eventDate.value,
|
start: eventDate.value,
|
||||||
backgroundColor: '#4CAF50' // 일반 이벤트 색상
|
backgroundColor: '#4CAF50' // 일반 이벤트 색상
|
||||||
});
|
});
|
||||||
closeModal();
|
closeModal(); // 모달 닫기
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 이벤트 유효성 검사 함수
|
||||||
const checkEvent = () => {
|
const checkEvent = () => {
|
||||||
|
// 제목과 날짜가 비어있는지 확인
|
||||||
eventAlert.value = isEmpty(eventTitle.value);
|
eventAlert.value = isEmpty(eventTitle.value);
|
||||||
eventDateAlert.value = isEmpty(eventDate.value);
|
eventDateAlert.value = isEmpty(eventDate.value);
|
||||||
|
// 하나라도 비어있으면 true 반환 (유효성 검사 실패)
|
||||||
return eventAlert.value || eventDateAlert.value;
|
return eventAlert.value || eventDateAlert.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 캘린더 옵션 설정
|
||||||
const calendarOptions = reactive({
|
const calendarOptions = reactive({
|
||||||
plugins: [dayGridPlugin, interactionPlugin],
|
plugins: [dayGridPlugin, interactionPlugin], // 사용할 플러그인
|
||||||
initialView: 'dayGridMonth',
|
initialView: 'dayGridMonth', // 초기 뷰 (월간)
|
||||||
headerToolbar: {
|
headerToolbar: { // 상단 툴바 구성
|
||||||
left: 'today',
|
left: 'today', // 왼쪽: 오늘 버튼
|
||||||
center: 'title',
|
center: 'title', // 중앙: 제목(연월)
|
||||||
right: 'prev,next',
|
right: 'prev,next', // 오른쪽: 이전/다음 버튼
|
||||||
},
|
},
|
||||||
locale: 'kr',
|
locale: 'kr', // 한국어 지역화
|
||||||
events: calendarEvents,
|
events: calendarEvents, // 표시할 이벤트 데이터
|
||||||
eventOrder: 'sortIdx',
|
eventOrder: 'sortIdx', // 이벤트 정렬 기준
|
||||||
selectable: true,
|
selectable: true, // 날짜 선택 가능 여부
|
||||||
dateClick: handleDateSelect,
|
dateClick: handleDateSelect, // 날짜 클릭 이벤트 핸들러
|
||||||
droppable: false,
|
droppable: false, // 드래그 앤 드롭 비활성화
|
||||||
eventDisplay: 'block',
|
eventDisplay: 'block', // 이벤트 표시 방식
|
||||||
|
|
||||||
|
// 커스텀 버튼 정의
|
||||||
customButtons: {
|
customButtons: {
|
||||||
prev: {
|
prev: {
|
||||||
text: 'PREV',
|
text: 'PREV', // 이전 버튼 텍스트
|
||||||
click: () => moveCalendar(1),
|
click: () => moveCalendar(1), // 클릭 시 이전 달로 이동
|
||||||
},
|
},
|
||||||
today: {
|
today: {
|
||||||
text: 'TODAY',
|
text: 'TODAY', // 오늘 버튼 텍스트
|
||||||
click: () => moveCalendar(3),
|
click: () => moveCalendar(3), // 클릭 시 오늘로 이동
|
||||||
},
|
},
|
||||||
next: {
|
next: {
|
||||||
text: 'NEXT',
|
text: 'NEXT', // 다음 버튼 텍스트
|
||||||
click: () => moveCalendar(2),
|
click: () => moveCalendar(2), // 클릭 시 다음 달로 이동
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 달력 뷰 변경 감지
|
// 달력 뷰 변경 감지 (월 변경 시 데이터 다시 가져오기)
|
||||||
watch(() => fullCalendarRef.value?.getApi().currentData.viewTitle, async () => {
|
watch(() => fullCalendarRef.value?.getApi().currentData.viewTitle, async () => {
|
||||||
await fetchData();
|
await fetchData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
console.log(project)
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await fetchData();
|
await fetchData();
|
||||||
await userStore.userInfo();
|
await userStore.userInfo();
|
||||||
user.value = userStore.user;
|
user.value = userStore.user;
|
||||||
|
await projectStore.getProjectList();
|
||||||
|
project.value = projectStore.projectList;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -7,11 +7,11 @@
|
|||||||
|
|
||||||
<!-- 프로젝트 목록 -->
|
<!-- 프로젝트 목록 -->
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<div v-if="projectList.length === 0" class="text-center">
|
<div v-if="projectStore.projectList.length === 0" class="text-center">
|
||||||
<p class="text-muted mt-4">등록된 프로젝트가 없습니다.</p>
|
<p class="text-muted mt-4">등록된 프로젝트가 없습니다.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-for="post in projectList" :key="post.PROJCTSEQ" @click="openEditModal(post)" class="cursor-pointer">
|
<div v-for="post in projectStore.projectList" :key="post.PROJCTSEQ" @click="openEditModal(post)" class="cursor-pointer">
|
||||||
<ProjectCard
|
<ProjectCard
|
||||||
:title="post.PROJCTNAM"
|
:title="post.PROJCTNAM"
|
||||||
:description="post.PROJCTDES"
|
:description="post.PROJCTDES"
|
||||||
@ -161,6 +161,7 @@ import ArrInput from '@c/input/ArrInput.vue';
|
|||||||
import commonApi from '@/common/commonApi';
|
import commonApi from '@/common/commonApi';
|
||||||
import { useToastStore } from '@s/toastStore';
|
import { useToastStore } from '@s/toastStore';
|
||||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
||||||
|
import { useProjectStore } from '@/stores/useProjectStore';
|
||||||
import $api from '@api';
|
import $api from '@api';
|
||||||
import SaveButton from '@c/button/SaveBtn.vue';
|
import SaveButton from '@c/button/SaveBtn.vue';
|
||||||
import BackButton from '@c/button/BackBtn.vue'
|
import BackButton from '@c/button/BackBtn.vue'
|
||||||
@ -169,10 +170,10 @@ const dayjs = inject('dayjs');
|
|||||||
const today = dayjs().format('YYYY-MM-DD');
|
const today = dayjs().format('YYYY-MM-DD');
|
||||||
const toastStore = useToastStore();
|
const toastStore = useToastStore();
|
||||||
const userStore = useUserInfoStore();
|
const userStore = useUserInfoStore();
|
||||||
|
const projectStore = useProjectStore();
|
||||||
|
|
||||||
// 상태 관리
|
// 상태 관리
|
||||||
const user = ref(null);
|
const user = ref(null);
|
||||||
const projectList = ref([]);
|
|
||||||
const selectedCategory = ref(null);
|
const selectedCategory = ref(null);
|
||||||
const searchText = ref('');
|
const searchText = ref('');
|
||||||
|
|
||||||
@ -212,17 +213,6 @@ const { yearCategory, colorList } = commonApi({
|
|||||||
loadYearCategory: true,
|
loadYearCategory: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 프로젝트 목록 조회
|
|
||||||
const getProjectList = async () => {
|
|
||||||
const res = await $api.get('project/select', {
|
|
||||||
params: {
|
|
||||||
searchKeyword : searchText.value,
|
|
||||||
category : selectedYear.value,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
projectList.value = res.data.data.projectList;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 검색 처리
|
// 검색 처리
|
||||||
const search = async (searchKeyword) => {
|
const search = async (searchKeyword) => {
|
||||||
searchText.value = searchKeyword.trim();
|
searchText.value = searchKeyword.trim();
|
||||||
@ -237,6 +227,10 @@ const selectedYear = computed(() => {
|
|||||||
return yearCategory.value.find(item => item.value === selectedCategory.value)?.label || null;
|
return yearCategory.value.find(item => item.value === selectedCategory.value)?.label || null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 프로젝트 목록 조회
|
||||||
|
const getProjectList = async () => {
|
||||||
|
await projectStore.getProjectList(searchText.value, selectedYear.value);
|
||||||
|
};
|
||||||
|
|
||||||
// 카테고리 변경 감지
|
// 카테고리 변경 감지
|
||||||
watch(selectedCategory, async () => {
|
watch(selectedCategory, async () => {
|
||||||
@ -250,6 +244,20 @@ const openCreateModal = () => {
|
|||||||
|
|
||||||
const closeCreateModal = () => {
|
const closeCreateModal = () => {
|
||||||
isCreateModalOpen.value = false;
|
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) {
|
if (res.status === 200) {
|
||||||
toastStore.onToast('프로젝트가 등록되었습니다.', 's');
|
toastStore.onToast('프로젝트가 등록되었습니다.', 's');
|
||||||
closeCreateModal();
|
closeCreateModal();
|
||||||
location.reload();
|
getProjectList();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -307,7 +315,7 @@ const allColors = computed(() => {
|
|||||||
|
|
||||||
// 변경된 내용 있는지 확인
|
// 변경된 내용 있는지 확인
|
||||||
const hasChanges = 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;
|
if (!original) return false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -32,7 +32,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import $api from '@api';
|
import $api from '@api';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import { ref } from 'vue';
|
import { nextTick, ref } from 'vue';
|
||||||
import UserFormInput from '@c/input/UserFormInput.vue';
|
import UserFormInput from '@c/input/UserFormInput.vue';
|
||||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
||||||
|
|
||||||
@ -70,10 +70,11 @@
|
|||||||
password: password.value,
|
password: password.value,
|
||||||
remember: remember.value,
|
remember: remember.value,
|
||||||
}, { headers: { isLogin: true } })
|
}, { headers: { isLogin: true } })
|
||||||
.then(res => {
|
.then(async res => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
userStore.userInfo();
|
userStore.userInfo();
|
||||||
router.push('/');
|
await nextTick();
|
||||||
|
router.push('/')
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
|
|||||||
30
src/stores/useProjectStore.js
Normal file
30
src/stores/useProjectStore.js
Normal file
@ -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 };
|
||||||
|
});
|
||||||
@ -140,7 +140,6 @@ import { ref, onMounted, computed } from 'vue';
|
|||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
||||||
import axios from '@api';
|
import axios from '@api';
|
||||||
import { formattedDate } from '@/common/formattedDate.js';
|
|
||||||
|
|
||||||
// 게시물 데이터 상태
|
// 게시물 데이터 상태
|
||||||
const profileName = ref('');
|
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 { 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 {
|
try {
|
||||||
const response = await axios.post(`board/${currentBoardId.value}/comment`, {
|
const response = await axios.post(`board/${currentBoardId.value}/comment`, {
|
||||||
LOCBRDSEQ: currentBoardId.value,
|
LOCBRDSEQ: currentBoardId.value,
|
||||||
LOCCMTRPY: comment,
|
LOCCMTRPY: comment,
|
||||||
LOCCMTPWD: password,
|
LOCCMTPWD: isCheck ? password : '',
|
||||||
LOCCMTPNT: 1,
|
LOCCMTPNT: 1,
|
||||||
LOCBRDTYP // 익명 여부 전달
|
LOCBRDTYP
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
console.log('댓글 작성 성공:', response.data.message);
|
console.log('댓글 작성 성공:', response.data.message);
|
||||||
|
passwordAlert.value = '';
|
||||||
|
commentAlert.value = '';
|
||||||
await fetchComments();
|
await fetchComments();
|
||||||
} else {
|
} else {
|
||||||
console.log('댓글 작성 실패:', response.data.message);
|
console.error('댓글 작성 실패:', response.data.message);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('댓글 작성 중 오류 발생:', error);
|
console.error('댓글 작성 중 오류 발생:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// 대댓글 추가 (부모 `BoardCommentList`로부터 이벤트 받아서 처리)
|
// 대댓글 추가 (부모 `BoardCommentList`로부터 이벤트 받아서 처리)
|
||||||
const handleCommentReply = async (reply) => {
|
const handleCommentReply = async (reply) => {
|
||||||
try {
|
try {
|
||||||
// 익명 여부 체크 (체크박스가 체크되었을 경우 LOCBRDTYP을 300102로 설정)
|
const response = await axios.post(`board/${currentBoardId.value}/comment`, {
|
||||||
|
|
||||||
const requestBody = {
|
|
||||||
LOCBRDSEQ: currentBoardId.value,
|
LOCBRDSEQ: currentBoardId.value,
|
||||||
LOCCMTRPY: reply.comment,
|
LOCCMTRPY: reply.comment,
|
||||||
LOCCMTPWD: reply.password || null,
|
LOCCMTPWD: reply.password || null,
|
||||||
LOCCMTPNT: reply.parentId,
|
LOCCMTPNT: reply.parentId,
|
||||||
LOCBRDTYP: reply.isCheck ? "300102" : null
|
LOCBRDTYP: reply.isCheck ? "300102" : null
|
||||||
};
|
});
|
||||||
console.log(requestBody)
|
|
||||||
const response = await axios.post(`board/${currentBoardId.value}/comment`, requestBody);
|
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
if (response.data.code === 200) {
|
if (response.data.code === 200) { // 서버 응답 코드도 확인
|
||||||
console.log('✅ 대댓글 작성 성공:', response.data);
|
console.log('대댓글 작성 성공:', response.data);
|
||||||
await fetchComments();
|
await fetchComments(); // 댓글 목록 새로고침
|
||||||
} else {
|
} else {
|
||||||
console.log('❌ 대댓글 작성 실패 - 서버 응답:', response.data);
|
console.log('대댓글 작성 실패 - 서버 응답:', response.data);
|
||||||
alert('대댓글 작성에 실패했습니다.');
|
alert('대댓글 작성에 실패했습니다.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('🚨 대댓글 작성 중 오류 발생:', error);
|
console.error('대댓글 작성 중 오류 발생:', error);
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
console.error('📌 서버 응답 에러:', error.response.data);
|
console.error('서버 응답 에러:', error.response.data);
|
||||||
}
|
}
|
||||||
alert('❌ 대댓글 작성 중 오류가 발생했습니다.');
|
alert('대댓글 작성 중 오류가 발생했습니다.');
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// 게시글 수정 버튼 클릭
|
// 게시글 수정 버튼 클릭
|
||||||
const editClick = (unknown) => {
|
const editClick = (unknown) => {
|
||||||
@ -484,6 +497,7 @@ const editComment = (comment) => {
|
|||||||
|
|
||||||
// 익명일 경우 비밀번호 입력창 활성화
|
// 익명일 경우 비밀번호 입력창 활성화
|
||||||
if (unknown.value) {
|
if (unknown.value) {
|
||||||
|
console.log('익명 코멘트인가?')
|
||||||
toggleCommentPassword(comment, "edit");
|
toggleCommentPassword(comment, "edit");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -736,6 +750,14 @@ const handleCommentDeleted = (deletedCommentId) => {
|
|||||||
console.error("❌ 삭제할 댓글을 찾을 수 없음:", 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));
|
const formattedBoardDate = computed(() => formattedDate(date.value));
|
||||||
|
|
||||||
// 컴포넌트 마운트 시 데이터 로드
|
// 컴포넌트 마운트 시 데이터 로드
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<Calendar />
|
<CommuteCalendar />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import Calendar from '@c/commuters/Calendar.vue';
|
import CommuteCalendar from '@c/commuters/CommuteCalendar.vue';
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user