diff --git a/.env.dev b/.env.dev
index 5702020..4da29f6 100644
--- a/.env.dev
+++ b/.env.dev
@@ -1,6 +1,6 @@
VITE_DOMAIN = https://192.168.0.251:5173/
# VITE_LOGIN_URL = http://localhost:10325/ms/
-# VITE_FILE_URL = http://localhost:10325/ms/
-VITE_API_URL = https://192.168.0.251:10325/api/
-VITE_TEST_URL = https://192.168.0.251:10325/test/
+VITE_SERVER = https://192.168.0.251:10300/
+VITE_API_URL = https://192.168.0.251:10300/api/
+VITE_TEST_URL = https://192.168.0.251:10300/test/
VITE_KAKAO_MAP_KEY=6f092e8f45ee81186bb6d8408f66a492
\ No newline at end of file
diff --git a/.env.mine b/.env.mine
index 6f5a98c..07aa984 100644
--- a/.env.mine
+++ b/.env.mine
@@ -1,6 +1,6 @@
VITE_DOMAIN = http://localhost:5173/
# VITE_LOGIN_URL = http://localhost:10325/ms/
-# VITE_FILE_URL = http://localhost:10325/ms/
+VITE_SERVER = http://localhost:10325/
VITE_API_URL = http://localhost:10325/api/
VITE_TEST_URL = http://localhost:10325/test/
VITE_KAKAO_MAP_KEY=6f092e8f45ee81186bb6d8408f66a492
\ No newline at end of file
diff --git a/index.html b/index.html
index 3a0ef47..3859f38 100644
--- a/index.html
+++ b/index.html
@@ -32,7 +32,6 @@
-
diff --git a/jenkinsfile b/jenkinsfile
new file mode 100644
index 0000000..d554d83
--- /dev/null
+++ b/jenkinsfile
@@ -0,0 +1,41 @@
+pipeline {
+ agent any
+ tools {
+ nodejs 'nodejs22'
+ }
+ stages {
+ stage('Build'){
+ steps {
+ bat 'npm install'
+ bat 'npm ci'
+ bat 'npm run build'
+ }
+ }
+ stage('Deploy') {
+ steps {
+ // 로컬 Nginx 서버에 빌드 파일 배포
+ bat '''
+ :: 루트경로 설정
+ set NGINX_ROOT=C:\\nginx\\html
+
+ :: 기존 빌드 삭제
+ if exist "%NGINX_ROOT%\\dist" rmdir /s /q "%NGINX_ROOT%\\dist"
+
+ :: 빌드 파일 복사
+ xcopy /s /y dist\\* "%NGINX_ROOT%\\dist\\"
+ '''
+ }
+ }
+ stage('Restart Server!') {
+ steps {
+ // 로컬 Nginx 서버 재실행
+ bat '''
+ net stop localnginx
+ ping -n 5 127.0.0.1 > nul
+ net start localnginx
+ ping -n 5 127.0.0.1 > nul
+ '''
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 9640429..037bc66 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"scripts": {
"dev": "vite --host 0.0.0.0 --mode dev",
"mine": "vite --host 0.0.0.0 --mode mine",
- "build": "vite build --mode prod",
+ "build": "vite build --mode dev",
"preview": "vite preview",
"lint": "eslint . --fix",
"format": "prettier --write src/"
diff --git a/public/css/custom.css b/public/css/custom.css
index 182e491..2ac47f0 100644
--- a/public/css/custom.css
+++ b/public/css/custom.css
@@ -3,22 +3,33 @@
/* 휴가 */
-.fc-daygrid-day-events {
- max-height: 100px !important;
- overflow-y: auto !important;
-}
+
/* 이벤트 선 없게 */
.fc-event {
border: none;
}
-/* 오전전반차 그래프 */
+/* 오전 반차 그래프 (왼쪽 절반) */
.fc-daygrid-event.half-day-am {
- width: calc(50% - 4px) !important;
+ width: 50% !important;
+ height: 8px !important;
+ border-radius: 2px !important;
+ font-size: 0px !important;
}
-/* 오후반차 그래프프 */
+/* 오후 반차 그래프 (오른쪽 절반) */
.fc-daygrid-event.half-day-pm {
- width: calc(50% - 4px) !important;
- margin-left: auto !important
+ width: 50% !important;
+ height: 8px !important;
+ margin-left: auto !important;
+ border-radius: 2px !important;
+ font-size: 0px !important;
+}
+/* 연차 그래프 (풀풀) */
+.fc-daygrid-event.full-day {
+ width: 100% !important;
+ height: 8px !important;
+ margin-left: auto !important;
+ border-radius: 2px !important;
+ font-size: 0px !important;
}
/* 공휴일,일요일 색상 */
.fc-day-sun .fc-daygrid-day-number,
@@ -39,8 +50,8 @@
.flatpickr-calendar:after {
display: none !important;
}
- /* 기본 스타일은 그대로 두고, 데이트피커 인풋의 추가 스타일 정의 */
- .fc-toolbar-title {
+/* 기본 스타일은 그대로 두고, 데이트피커 인풋의 추가 스타일 정의 */
+.fc-toolbar-title {
cursor: pointer;
}
/* 클릭 가능한 날짜 (오늘 + 미래) */
@@ -84,7 +95,6 @@ opacity: 0.6; /* 흐려 보이게 */
/* 본인 모달 */
-
/* 닫기 버튼 */
.close-btn {
position: absolute;
@@ -109,7 +119,6 @@ opacity: 0.6; /* 흐려 보이게 */
/* 선물하기 모달 */
-
/* 연차 개수 버튼 */
.count-btn {
font-size: 18px;
@@ -127,7 +136,6 @@ opacity: 0.6; /* 흐려 보이게 */
background: #cccccc;
cursor: not-allowed;
}
-
/* 버튼 컨테이너 (우측 정렬) */
.custom-button-container {
display: flex;
@@ -141,18 +149,17 @@ opacity: 0.6; /* 흐려 보이게 */
padding: 10px; /* 크기 조정 */
cursor: pointer; /* 클릭 가능하도록 변경 */
}
-
/* 아이콘 색상 변경 (기본) */
.custom-button i {
color: #282538; /* 기본 아이콘 색상 */
font-size: 25px; /* 아이콘 크기 */
}
-
/* 버튼 호버 효과 */
.custom-button:hover i {
color: #ff0800; /* 호버 시 아이콘 색상 변경 */
}
+
.grayscaleImg {
filter: grayscale(100%);
}
diff --git a/public/img/mbti/enfj.png b/public/img/mbti/enfj.png
new file mode 100644
index 0000000..2d85491
Binary files /dev/null and b/public/img/mbti/enfj.png differ
diff --git a/public/img/mbti/enfp.png b/public/img/mbti/enfp.png
new file mode 100644
index 0000000..abc6c69
Binary files /dev/null and b/public/img/mbti/enfp.png differ
diff --git a/public/img/mbti/entj.png b/public/img/mbti/entj.png
new file mode 100644
index 0000000..7ef1341
Binary files /dev/null and b/public/img/mbti/entj.png differ
diff --git a/public/img/mbti/entp.png b/public/img/mbti/entp.png
new file mode 100644
index 0000000..84b2a11
Binary files /dev/null and b/public/img/mbti/entp.png differ
diff --git a/public/img/mbti/esfj.png b/public/img/mbti/esfj.png
new file mode 100644
index 0000000..0548975
Binary files /dev/null and b/public/img/mbti/esfj.png differ
diff --git a/public/img/mbti/esfp.png b/public/img/mbti/esfp.png
new file mode 100644
index 0000000..a3932e6
Binary files /dev/null and b/public/img/mbti/esfp.png differ
diff --git a/public/img/mbti/est.png b/public/img/mbti/est.png
new file mode 100644
index 0000000..720aa00
Binary files /dev/null and b/public/img/mbti/est.png differ
diff --git a/public/img/mbti/estp.png b/public/img/mbti/estp.png
new file mode 100644
index 0000000..036e95f
Binary files /dev/null and b/public/img/mbti/estp.png differ
diff --git a/public/img/mbti/infj.png b/public/img/mbti/infj.png
new file mode 100644
index 0000000..4660186
Binary files /dev/null and b/public/img/mbti/infj.png differ
diff --git a/public/img/mbti/infp.png b/public/img/mbti/infp.png
new file mode 100644
index 0000000..442b8dd
Binary files /dev/null and b/public/img/mbti/infp.png differ
diff --git a/public/img/mbti/intj.png b/public/img/mbti/intj.png
new file mode 100644
index 0000000..39b06a2
Binary files /dev/null and b/public/img/mbti/intj.png differ
diff --git a/public/img/mbti/intp.png b/public/img/mbti/intp.png
new file mode 100644
index 0000000..8dbb981
Binary files /dev/null and b/public/img/mbti/intp.png differ
diff --git a/public/img/mbti/isfj.png b/public/img/mbti/isfj.png
new file mode 100644
index 0000000..7dad009
Binary files /dev/null and b/public/img/mbti/isfj.png differ
diff --git a/public/img/mbti/isfp.png b/public/img/mbti/isfp.png
new file mode 100644
index 0000000..61e4bad
Binary files /dev/null and b/public/img/mbti/isfp.png differ
diff --git a/public/img/mbti/istj.png b/public/img/mbti/istj.png
new file mode 100644
index 0000000..4924396
Binary files /dev/null and b/public/img/mbti/istj.png differ
diff --git a/public/img/mbti/istp.png b/public/img/mbti/istp.png
new file mode 100644
index 0000000..276f39c
Binary files /dev/null and b/public/img/mbti/istp.png differ
diff --git a/public/js/main.js b/public/js/main.js
index c118e86..102f787 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -7,12 +7,6 @@
//-----------------
let menu, animate;
-<<<<<<< HEAD
-var menu, animate;
-(function () {
- // Initialize menu
- //-----------------
-=======
let layoutMenuEl = document.querySelectorAll('#layout-menu');
layoutMenuEl.forEach(function (element) {
menu = new Menu(element, {
@@ -23,7 +17,6 @@ var menu, animate;
window.Helpers.scrollToActive((animate = false));
window.Helpers.mainMenu = menu;
});
->>>>>>> board-comment
// Initialize menu togglers and bind click on each
let menuToggler = document.querySelectorAll('.layout-menu-toggle');
@@ -118,5 +111,7 @@ var menu, animate;
// If current layout is vertical and current window screen is > small
// Auto update menu collapsed/expanded based on the themeConfig
- window.Helpers.setCollapsed(true, false);
+ // 250304 pc 화면에서 메뉴바 고정을 위해 false 처리
+ window.Helpers.setCollapsed(false, false);
+ //window.Helpers.setCollapsed(true, false);
})();
diff --git a/src/common/common.js b/src/common/common.js
index d1e4b91..9acd369 100644
--- a/src/common/common.js
+++ b/src/common/common.js
@@ -73,6 +73,14 @@ const common = {
seconds: zeroFormat(date.getSeconds()),
};
},
+
+ isNotEmpty(obj) {
+ if (obj === null || obj === undefined) return false;
+ if (typeof obj === 'string' && obj.trim() === '') return false;
+ if ((Array.isArray(obj) || obj === Object(obj)) && Object.keys(obj).length === 0) return false;
+
+ return true;
+ },
};
export default {
diff --git a/src/components/board/BoardComment.vue b/src/components/board/BoardComment.vue
index ded58dc..c7e9220 100644
--- a/src/components/board/BoardComment.vue
+++ b/src/components/board/BoardComment.vue
@@ -11,43 +11,42 @@
:isLike="!isLike"
:isCommentPassword="isCommentPassword"
:isCommentProfile="true"
- @editClick="aaaa"
+ @editClick="handleEditClick"
@deleteClick="$emit('deleteClick', comment)"
@updateReaction="handleUpdateReaction"
- />
-
-
+ />
-
+
-
-
-
-
+
{{ comment.content }}
-
+
+
+
@@ -58,10 +57,6 @@
:key="child.commentId"
class="mt-8 pt-6 ps-10 border-top"
>
-
-
-
-
child.isCommentPassword: {{ child.isCommentPassword }}
$emit('submitEdit', comment, editedContent)"
@cancelEdit="$emit('cancelEdit', child)"
@submitComment="submitComment"
@updateReaction="handleUpdateReaction"
+ @submitPassword="$emit('submitPassword', child, password)"
+ @update:password="$emit('update:password', $event)"
/>
@@ -114,20 +114,29 @@ const props = defineProps({
type: Boolean,
default: false
},
+ isDeleted: {
+ type: Boolean,
+ default: false
+ },
isCommentPassword: {
type: Boolean,
default: false,
},
passwordCommentAlert: {
type: String,
- default: false
- }
+ default: ''
+ },
+ currentPasswordCommentId: {
+ type: Number
+ },
+ password:{
+ type: String
+ },
});
// emits 정의
-const emit = defineEmits(['submitComment', 'updateReaction', 'editClick', 'deleteClick', 'submitPassword', 'submitEdit', 'cancelEdit']);
+const emit = defineEmits(['submitComment', 'updateReaction', 'editClick', 'deleteClick', 'submitPassword', 'submitEdit', 'cancelEdit', 'update:password']);
-const password = ref('');
const localEditedContent = ref(props.comment.content);
// 댓글 입력 창 토글
@@ -154,8 +163,7 @@ const handleUpdateReaction = (reactionData) => {
// 비밀번호 확인
const logPasswordAndEmit = () => {
- emit('submitPassword', props.comment, password.value);
- password.value = "";
+ emit('submitPassword', props.comment, props.password);
};
watch(() => props.comment.isEditTextarea, (newVal) => {
@@ -164,13 +172,26 @@ watch(() => props.comment.isEditTextarea, (newVal) => {
}
});
+// watch(() => props.comment.isDeleted, () => {
+// console.log("BoardComment - isDeleted 상태 변경됨:", newVal);
+
+// if (newVal) {
+// localEditedContent.value = "댓글이 삭제되었습니다."; // UI 반영
+// props.comment.isEditTextarea = false;
+// }
+// });
+
// 수정버튼
const submitEdit = () => {
emit('submitEdit', props.comment, localEditedContent.value);
};
-const aaaa = () => {
+const handleEditClick = () => {
emit('editClick', props.comment);
}
+const handleReplyEditClick = (comment) => {
+ emit('editClick', comment);
+}
+
diff --git a/src/components/board/BoardCommentArea.vue b/src/components/board/BoardCommentArea.vue
index 4f2fca2..283ce8b 100644
--- a/src/components/board/BoardCommentArea.vue
+++ b/src/components/board/BoardCommentArea.vue
@@ -11,13 +11,9 @@
-->
-
+
{{ commentAlert }}
+ {{ textAlert }}
@@ -26,12 +22,7 @@
-
+
@@ -62,7 +51,8 @@
+
diff --git a/src/components/board/BoardCommentList.vue b/src/components/board/BoardCommentList.vue
index 3ee4624..0987a4c 100644
--- a/src/components/board/BoardCommentList.vue
+++ b/src/components/board/BoardCommentList.vue
@@ -10,16 +10,19 @@
:comment="comment"
:isCommentAuthor="comment.isCommentAuthor"
:isEditTextarea="comment.isEditTextarea"
+ :isDeleted="isDeleted"
:isCommentPassword="isCommentPassword"
- :passwordCommentAlert="passwordCommentAlert"
+ :passwordCommentAlert="passwordCommentAlert || ''"
+ :currentPasswordCommentId="currentPasswordCommentId"
+ :password="password"
@editClick="handleEditClick"
@deleteClick="handleDeleteClick"
@submitPassword="submitPassword"
@submitComment="submitComment"
- @commentDeleted="handleCommentDeleted"
@submitEdit="handleSubmitEdit"
@cancelEdit="handleCancelEdit"
@updateReaction="(reactionData) => handleUpdateReaction(reactionData, comment.commentId, comment.boardId)"
+ @update:password="updatePassword"
/>
@@ -51,13 +54,23 @@ const props = defineProps({
type: Boolean,
default: false,
},
+ isDeleted: {
+ type: Boolean,
+ default: false,
+ },
passwordCommentAlert: {
type: String,
- default: false
- }
+ default: ''
+ },
+ currentPasswordCommentId: {
+ type: Number
+ },
+ password:{
+ type: String
+ },
});
-const emit = defineEmits(['submitComment', 'updateReaction', 'editClick', 'deleteClick', 'submitPassword', 'clearPassword']);
+const emit = defineEmits(['submitComment', 'updateReaction', 'editClick', 'deleteClick', 'submitPassword', 'clearPassword','submitEdit', 'update:password']);
const submitComment = (replyData) => {
emit('submitComment', replyData);
@@ -78,7 +91,11 @@ const submitPassword = (comment, password) => {
};
const handleEditClick = (comment) => {
- emit('editClick', comment);
+ if (comment.parentId) {
+ emit('editClick', comment); // 대댓글
+ } else {
+ emit('editClick', comment); // 댓글
+ }
};
const handleSubmitEdit = (comment, editedContent) => {
@@ -100,4 +117,8 @@ const handleCancelEdit = (comment) => {
emit('cancelEdit', comment); // 댓글 수정 취소
}
};
+
+const updatePassword = (newPassword) => {
+ emit('update:password', newPassword);
+};
diff --git a/src/components/board/BoardProfile.vue b/src/components/board/BoardProfile.vue
index c3c4f91..25ae89d 100644
--- a/src/components/board/BoardProfile.vue
+++ b/src/components/board/BoardProfile.vue
@@ -2,8 +2,9 @@
-

+
+
{{ profileName }}
@@ -22,8 +23,7 @@
-
-
+
@@ -34,18 +34,23 @@
:boardId="boardId"
:comment="comment"
@updateReaction="handleUpdateReaction"
- >
-
+ />
-
-
diff --git a/src/components/button/BoardRecommendBtn.vue b/src/components/button/BoardRecommendBtn.vue
index e297eb7..8ffa805 100644
--- a/src/components/button/BoardRecommendBtn.vue
+++ b/src/components/button/BoardRecommendBtn.vue
@@ -13,7 +13,7 @@ import { ref, computed } from 'vue';
const props = defineProps({
comment: {
type: Object,
- required: true,
+ default: () => ({}),
},
likeClicked : {
type : Boolean,
@@ -36,7 +36,7 @@ const props = defineProps({
required: true,
},
commentId: {
- type: Number,
+ type: [Number, null],
default: null,
},
likeCount: {
diff --git a/src/components/button/EditBtn.vue b/src/components/button/EditBtn.vue
index 9987ddb..d3437b0 100644
--- a/src/components/button/EditBtn.vue
+++ b/src/components/button/EditBtn.vue
@@ -1,13 +1,30 @@
-
-
diff --git a/src/components/button/HalfDayButtons.vue b/src/components/button/HalfDayButtons.vue
index 9bb125f..c4fa6ee 100644
--- a/src/components/button/HalfDayButtons.vue
+++ b/src/components/button/HalfDayButtons.vue
@@ -20,7 +20,8 @@
-
+
✔
@@ -28,26 +29,28 @@
+const props = defineProps({
+ isToggleEnabled: {
+ type: Boolean,
+ default: false,
+ },
+});
+
+const buttonClass = ref("bx bx-edit");
+
+const toggleText = () => {
+ if (props.isToggleEnabled) {
+ buttonClass.value = buttonClass.value === "bx bx-edit" ? "bx bx-x" : "bx bx-edit";
+ }
+};
+
+const resetButton = () => {
+ buttonClass.value = "bx bx-edit";
+};
+
+defineExpose({ resetButton });
+
\ No newline at end of file
diff --git a/src/components/input/FormFile.vue b/src/components/input/FormFile.vue
index bc45bea..833c6b0 100644
--- a/src/components/input/FormFile.vue
+++ b/src/components/input/FormFile.vue
@@ -1,11 +1,12 @@
-
+
@@ -21,7 +22,7 @@ import { ref ,computed} from 'vue';
import { fileMsg } from '@/common/msgEnum';
// Props
-const prop = defineProps({
+const props = defineProps({
title: {
type: String,
default: '라벨',
@@ -29,7 +30,7 @@ const prop = defineProps({
},
name: {
type: String,
- default: 'nameplz',
+ default: 'fileInput',
required: true,
},
isAlert: {
@@ -38,12 +39,13 @@ const prop = defineProps({
required: false,
},
});
+const inputId = computed(() => props.name || 'defaultFileInput');
const emits = defineEmits(['update:data', 'update:isValid']);
const MAX_TOTAL_SIZE = 5 * 1024 * 1024; // 5MB
const MAX_FILE_COUNT = 5; // 최대 파일 개수
-const ALLOWED_FILE_TYPES = ['image/jpeg', 'image/png', 'application/pdf']; // 허용된 파일 유형
+const ALLOWED_FILE_TYPES = []; // 모든 파일을 허용
const showError = ref(false);
const fileMsgKey = ref(''); // 에러 메시지 키
@@ -51,9 +53,12 @@ const fileMsgKey = ref(''); // 에러 메시지 키
const changeHandler = (event) => {
const files = Array.from(event.target.files);
const totalSize = files.reduce((sum, file) => sum + file.size, 0);
- const invalidFiles = files.filter(file => !ALLOWED_FILE_TYPES.includes(file.type));
- // 파일 검증 로직
+ // ALLOWED_FILE_TYPES가 비어있으면 모든 파일 허용
+ const invalidFiles = ALLOWED_FILE_TYPES.length > 0
+ ? files.filter(file => !ALLOWED_FILE_TYPES.includes(file.type))
+ : [];
+
if (totalSize > MAX_TOTAL_SIZE) {
showError.value = true;
fileMsgKey.value = 'FileMaxSizeMsg';
diff --git a/src/components/input/FormInput.vue b/src/components/input/FormInput.vue
index a51c16c..d1ad6fd 100644
--- a/src/components/input/FormInput.vue
+++ b/src/components/input/FormInput.vue
@@ -15,6 +15,7 @@
:disabled="disabled"
:min="min"
@focusout="$emit('focusout', modelValue)"
+ @input="handleInput"
/>
{{ title }}을 확인해주세요.
@@ -92,11 +93,6 @@ const inputValue = ref(props.modelValue);
// 부모로 데이터 업데이트
watch(inputValue, (newValue) => {
emits('update:modelValue', newValue);
-
- // 값이 입력될 때 `alert`를 false로 설정
- if (newValue.trim() !== '') {
- emits('update:alert', false);
- }
});
// 초기값 동기화
@@ -106,6 +102,13 @@ watch(() => props.modelValue, (newValue) => {
}
});
+const handleInput = (event) => {
+ const newValue = event.target.value.slice(0, props.maxlength);
+
+ if (newValue.trim() !== '') {
+ emits('update:alert', false);
+ }
+};
diff --git a/src/components/input/FormSelect.vue b/src/components/input/FormSelect.vue
index 1115ea7..1da67e5 100644
--- a/src/components/input/FormSelect.vue
+++ b/src/components/input/FormSelect.vue
@@ -4,19 +4,30 @@
{{ title }}
*
-
-
\ No newline at end of file
+
+
+const selected = computed(() => {
+ const selectedItem = props.data.find(item =>
+ props.isCommon ? item.value === selectData.value : props.data.indexOf(item) === selectData.value
+ );
+ return selectedItem ? selectedItem.label : null;
+});
+
+
diff --git a/src/components/list/ProjectCard.vue b/src/components/list/ProjectCard.vue
index e81e4fb..7af8059 100644
--- a/src/components/list/ProjectCard.vue
+++ b/src/components/list/ProjectCard.vue
@@ -38,7 +38,7 @@
{{ address }} {{ addressdtail }}
-
+
@@ -46,8 +46,7 @@
v-if="coordinates"
:lat="coordinates.lat"
:lng="coordinates.lng"
- :draggable="false"
- class="w-100 h-px-200"
+ class="w-px-200 h-px-200"
>
{
isEditModalOpen.value = false;
};
+// 변경된 내용 있는지 확인
+const hasChanges = computed(() => {
+ return selectedProject.value.PROJCTNAM !== props.title ||
+ selectedProject.value.PROJCTSTR !== props.strdate ||
+ selectedProject.value.PROJCTEND !== props.enddate ||
+ selectedProject.value.PROJCTZIP !== props.addressZip ||
+ selectedProject.value.PROJCTARR !== props.address ||
+ selectedProject.value.PROJCTDTL !== props.addressdtail ||
+ selectedProject.value.PROJCTDES !== props.description ||
+ selectedProject.value.PROJCTCOL !== props.projctCol;
+});
+
// 프로젝트 수정
const handleUpdate = () => {
nameAlert.value = selectedProject.value.PROJCTNAM.trim() === '';
@@ -318,6 +329,11 @@ const handleUpdate = () => {
return;
}
+ if (!hasChanges.value) {
+ toastStore.onToast('변경된 내용이 없습니다.', 'e');
+ return;
+ }
+
$api.patch('project/update', {
projctSeq: selectedProject.value.PROJCTSEQ,
projctNam: selectedProject.value.PROJCTNAM,
@@ -336,7 +352,6 @@ const handleUpdate = () => {
closeEditModal();
// 상위 컴포넌트에 업데이트 알림
emit('update');
- window.location.reload()
}
});
};
diff --git a/src/components/projectlist/ProjectList.vue b/src/components/projectlist/ProjectList.vue
index 7df7b41..cc10764 100644
--- a/src/components/projectlist/ProjectList.vue
+++ b/src/components/projectlist/ProjectList.vue
@@ -49,6 +49,7 @@
:is-essential="true"
:is-label="true"
:is-common="true"
+ :is-color="true"
:data="colorList"
@update:data="color = $event"
/>
@@ -100,7 +101,7 @@
import { computed, ref, watch, onMounted, inject } from 'vue';
import SearchBar from '@c/search/SearchBar.vue';
import ProjectCard from '@c/list/ProjectCard.vue';
- import CategoryBtn from '@c/category/CategoryBtn.vue';
+ import CategoryBtn from '@/components/category/CategoryBtn.vue';
import WriteBtn from '@c/button/WriteBtn.vue';
import CenterModal from '@c/modal/CenterModal.vue';
import FormSelect from '@c/input/FormSelect.vue';
diff --git a/src/components/user/FindPassword.vue b/src/components/user/FindPassword.vue
index 0260908..09866eb 100644
--- a/src/components/user/FindPassword.vue
+++ b/src/components/user/FindPassword.vue
@@ -68,20 +68,21 @@
:is-alert="passwordcheckAlert"
@update:data="passwordcheck = $event"
@update:alert="passwordcheckAlert = $event"
- @blur="checkPw"
+ @input="checkPw"
:value="passwordcheck"
/>
{{ passwordcheckError }}
-
-
-
{{ pwErrMsg }}
+
+
+
+
{{ pwErrMsg }}
diff --git a/src/components/user/RegisterForm.vue b/src/components/user/RegisterForm.vue
index 2953ea3..5a5b557 100644
--- a/src/components/user/RegisterForm.vue
+++ b/src/components/user/RegisterForm.vue
@@ -92,6 +92,7 @@
:is-row="false"
:is-label="true"
:is-common="true"
+ :is-color="true"
:data="colorList"
@update:data="color = $event"
class="w-50"
@@ -118,6 +119,7 @@
:is-row="false"
:is-label="true"
:is-common="true"
+ :is-mbti="true"
:data="mbtiList"
@update:data="mbti = $event"
class="w-50"
diff --git a/src/components/user/UserList.vue b/src/components/user/UserList.vue
index 27f81e6..f921ce3 100644
--- a/src/components/user/UserList.vue
+++ b/src/components/user/UserList.vue
@@ -1,7 +1,7 @@
-
diff --git a/src/layouts/TheMenu.vue b/src/layouts/TheMenu.vue
index f82dba7..6c5c44c 100644
--- a/src/layouts/TheMenu.vue
+++ b/src/layouts/TheMenu.vue
@@ -23,9 +23,10 @@
LOCALNET
-
diff --git a/src/layouts/TheTop.vue b/src/layouts/TheTop.vue
index 9bd7296..ada3701 100644
--- a/src/layouts/TheTop.vue
+++ b/src/layouts/TheTop.vue
@@ -241,7 +241,7 @@
const user = ref(null);
//const baseUrl = $api.defaults.baseURL.replace(/api\/$/, '');
- const baseUrl = import.meta.env.BASE_URL;
+ const baseUrl = import.meta.env.VITE_SERVER;
const authStore = useAuthStore();
const userStore = useUserInfoStore();
diff --git a/src/views/board/BoardList.vue b/src/views/board/BoardList.vue
index 095e5e3..0a33b11 100644
--- a/src/views/board/BoardList.vue
+++ b/src/views/board/BoardList.vue
@@ -18,8 +18,8 @@
50개씩
-
-
+
-
{{ boardTitle }}
-
-
+
+
- 첨부파일
-
+ 첨부파일 ({{ attachments.length }}개)
-
+
@@ -74,21 +63,8 @@
:likeClicked="likeClicked"
:dislikeClicked="dislikeClicked"
@updateReaction="handleUpdateReaction"
- />
+ />
-
-
-
-
-
-
-
-
@@ -106,9 +81,12 @@
{
isCommentAuthor: reply.authorId === currentUserId.value,
}))
}));
- // console.log("✅ commentsWithAuthStatus 업데이트됨:", updatedComments);
return updatedComments;
});
+const attachments = ref([]);
+// 첨부파일 다운로드 URL 생성
+const downloadFile = async (attachment) => {
+ try {
+ const response = await axios.get(`board/download`, {
+ params: { path: attachment.path },
+ responseType: 'blob'
+ });
+
+ // Blob에서 파일 다운로드 링크 생성
+ const url = window.URL.createObjectURL(new Blob([response.data]));
+ const link = document.createElement('a');
+ link.href = url;
+ link.setAttribute('download', attachment.originalName + '.' + attachment.extension);
+ document.body.appendChild(link);
+ link.click();
+ link.remove();
+ window.URL.revokeObjectURL(url);
+ } catch (error) {
+ console.error('파일 다운로드 오류:', error);
+ alert('파일 다운로드 중 오류가 발생했습니다.');
+ }
+};
+
+
const password = ref('');
const passwordAlert = ref("");
const passwordCommentAlert = ref("");
const isPassword = ref(false);
const isCommentPassword = ref(false);
+const currentPasswordCommentId = ref(null);
const lastClickedButton = ref("");
const lastCommentClickedButton = ref("");
const isEditTextarea = ref(false);
-const commentAlert = ref('')
+const isDeleted = ref(true);
+const commentAlert = ref('');
+
+const updatePassword = (newPassword) => {
+ password.value = newPassword;
+};
const pagination = ref({
currentPage: 1,
@@ -210,21 +219,10 @@ const fetchBoardDetails = async () => {
const response = await axios.get(`board/${currentBoardId.value}`);
const data = response.data.data;
- // console.log(data)
-
- // API 응답 데이터 반영
- // const boardDetail = data.boardDetail || {};
profileName.value = data.author || '익명';
- // 익명확인하고 싶을때
- // profileName.value = '익명';
-
- // console.log("📌 게시글 작성자:", profileName.value); // 작성자 이름 출력
- // console.log("🔍 익명 여부 (unknown.value):", unknown.value); // 익명 여부 확인
-
-
- authorId.value = data.authorId; //게시글 작성자 id
+ authorId.value = data.authorId;
boardTitle.value = data.title || '제목 없음';
boardContent.value = data.content || '';
date.value = data.date || '';
@@ -233,6 +231,7 @@ const fetchBoardDetails = async () => {
dislikes.value = data.dislikeCount || 0;
attachment.value = data.hasAttachment || null;
commentNum.value = data.commentCount || 0;
+ attachments.value = data.attachments || [];
} catch (error) {
alert('게시물 데이터를 불러오는 중 오류가 발생했습니다.');
@@ -245,7 +244,6 @@ const handleUpdateReaction = async ({ boardId, commentId, isLike, isDislike }) =
await axios.post(`/board/${boardId}/${commentId}/reaction`, {
LOCBRDSEQ: boardId, // 게시글 id
LOCCMTSEQ: commentId, //댓글 id
- // MEMBERSEQ: 1, // 멤버아이디 지금은 1 나중에 수정해야함
LOCGOBGOD: isLike ? 'T' : 'F',
LOCGOBBAD: isDislike ? 'T' : 'F'
});
@@ -258,13 +256,9 @@ const handleUpdateReaction = async ({ boardId, commentId, isLike, isDislike }) =
likeClicked.value = isLike;
dislikeClicked.value = isDislike;
- // console.log(updatedData)
-
- // console.log("갱신된 데이터:", updatedData);
} catch (error) {
alert('오류가 발생했습니다.');
- // console.log('반응을 업데이트하는 중 오류가 발생했습니다.');
}
};
@@ -281,13 +275,10 @@ const handleCommentReaction = async ({ boardId, commentId, isLike, isDislike })
LOCGOBBAD: isDislike ? 'T' : 'F'
});
- // console.log("댓글 좋아요 API 응답 데이터:", response.data);
-
await fetchComments();
} catch (error) {
alert('오류가 발생했습니다.');
- // console.log('댓글 반응을 업데이트하는 중 오류 발생');
}
};
@@ -301,9 +292,6 @@ const fetchComments = async (page = 1) => {
page
}
});
-
- // console.log(response.data.data)
-
const commentsList = response.data.data.list.map(comment => ({
commentId: comment.LOCCMTSEQ, // 댓글 ID
boardId: comment.LOCBRDSEQ,
@@ -318,8 +306,12 @@ const fetchComments = async (page = 1) => {
createdAtRaw: new Date(comment.LOCCMTRDT), // 정렬용
createdAt: formattedDate(comment.LOCCMTRDT), // 표시용
children: [], // 대댓글을 담을 배열
+ updateAtRaw: comment.LOCCMTUDT,
+
}));
+ commentsList.sort((a, b) => b.createdAtRaw - a.createdAtRaw);
+
for (const comment of commentsList) {
if (!comment.commentId) continue;
@@ -327,8 +319,6 @@ const fetchComments = async (page = 1) => {
params: { LOCCMTPNT: comment.commentId }
});
- // console.log(`대댓글 데이터 (${comment.commentId}의 대댓글):`, replyResponse.data);
-
if (replyResponse.data.data) {
comment.children = replyResponse.data.data.map(reply => ({
author: reply.author || '익명',
@@ -368,23 +358,19 @@ const fetchComments = async (page = 1) => {
navigateLastPage: response.data.data.navigateLastPage // 페이지네이션에서 마지막 페이지 번호
};
- // console.log("📌 댓글 목록:", comments.value);
-
} catch (error) {
alert('오류가 발생했습니다.');
- // alert('댓글 목록 불러오기 오류:', error);
}
};
// 댓글 작성
-const handleCommentSubmit = async (data, isCheck) => {
+const handleCommentSubmit = async (data) => {
if (!data) {
- console.error("handleCommentSubmit: data가 undefined입니다.");
return;
}
- const { comment, password } = data;
+ const { comment, password, isCheck } = data;
if (!comment || comment.trim() === "") {
commentAlert.value = '댓글을 입력해주세요.';
@@ -404,25 +390,22 @@ const handleCommentSubmit = async (data, isCheck) => {
LOCCMTRPY: comment,
LOCCMTPWD: isCheck ? password : '',
LOCCMTPNT: 1,
- LOCBRDTYP: unknown.value ? "300102" : null
+ LOCBRDTYP: isCheck ? "300102" : null
});
if (response.status === 200) {
- // console.log('댓글 작성 성공:', response.data.message);
passwordAlert.value = '';
commentAlert.value = '';
await fetchComments();
} else {
- // console.error('댓글 작성 실패:', response.data.message);
alert("댓글 작성을 실패했습니다.")
}
} catch (error) {
- // console.error('댓글 작성 중 오류 발생:', error);
alert("오류가 발생했습니다.")
}
};
-// 대댓글 추가 (부모 `BoardCommentList`로부터 이벤트 받아서 처리)
+// 대댓글 추가
const handleCommentReply = async (reply) => {
try {
const response = await axios.post(`board/${currentBoardId.value}/comment`, {
@@ -434,22 +417,17 @@ const handleCommentReply = async (reply) => {
});
if (response.status === 200) {
- if (response.data.code === 200) { // 서버 응답 코드도 확인
- // console.log('대댓글 작성 성공:', response.data);
- await fetchComments(); // 댓글 목록 새로고침
+ if (response.data.code === 200) {
+ await fetchComments();
} else {
- // console.log('대댓글 작성 실패 - 서버 응답:', response.data);
alert('대댓글 작성을 실패했습니다.');
}
}
} catch (error) {
- // console.error('대댓글 작성 중 오류 발생:', error);
if (error.response) {
- // console.error('서버 응답 에러:', error.response.data);
alert("오류가 발생했습니다.");
}
alert("오류가 발생했습니다.");
-
}
}
@@ -486,45 +464,69 @@ const findCommentById = (commentId, commentsList) => {
return null;
};
-// 댓글 수정 버튼 클릭
+// 댓글 수정(대댓글 포함)
const editComment = (comment) => {
+ password.value = '';
+ passwordCommentAlert.value = '';
+ currentPasswordCommentId.value = null;
- // 부모 또는 대댓글을 찾아서 가져오기
const targetComment = findCommentById(comment.commentId, comments.value);
if (!targetComment) {
return;
}
- // 댓글 작성자가 현재 로그인한 사용자와 동일한지 확인
const isMyComment = comment.authorId === currentUserId.value;
const isAnonymous = comment.author === "익명";
if (isMyComment) {
- // 본인 댓글이면 바로 수정 모드 활성화
- targetComment.isEditTextarea = true;
- } else if (isAnonymous) {
- // 익명 댓글이면 비밀번호 입력창 띄우기
- toggleCommentPassword(comment, "edit");
+ if (targetComment.isEditTextarea) {
+ // 수정창이 열려 있는 상태에서 다시 수정 버튼을 누르면 초기화
+ targetComment.isEditTextarea = false;
+ currentPasswordCommentId.value = comment.commentId;
+ } else {
+ // 다른 모든 댓글의 수정창 닫기
+ closeAllEditTextareas();
+ // 현재 댓글만 수정 모드 활성화
+ targetComment.isEditTextarea = true;
+ }
+ } else if (isAnonymous) {
+ if (currentPasswordCommentId.value === comment.commentId) {
+ // 이미 비밀번호 입력 중이면 유지
+ return;
+ } else {
+ // 다른 모든 댓글의 수정창 닫기
+ closeAllEditTextareas();
+
+ // 비밀번호 입력
+ targetComment.isEditTextarea = false;
+ toggleCommentPassword(comment, "edit");
+ }
} else {
- // console.log("다른 사용자 댓글 - 수정 불가");
alert("수정이 불가능합니다");
}
}
+// 모든 댓글의 수정 창 닫기
+const closeAllEditTextareas = () => {
+ comments.value.forEach(comment => {
+ comment.isEditTextarea = false;
+ comment.children.forEach(reply => {
+ reply.isEditTextarea = false;
+ });
+ });
+};
+
// 댓글 삭제 버튼 클릭
const deleteComment = async (comment) => {
- // 익명 사용자인 경우
const isMyComment = comment.authorId === currentUserId.value;
if (unknown.value && !isMyComment) {
if (comment.isEditTextarea) {
- // 현재 수정 중이라면 수정 모드를 끄고, 삭제 비밀번호 입력창을 띄움
comment.isEditTextarea = false;
comment.isCommentPassword = true;
} else {
- // 수정 중이 아니면 기존의 삭제 비밀번호 입력창을 띄우는 로직 실행
toggleCommentPassword(comment, "delete");
}
} else {
@@ -532,19 +534,21 @@ const deleteComment = async (comment) => {
}
};
-// 익명 비밀번호 창 토글
+// 익명 댓글 비밀번호 창 토글
const toggleCommentPassword = (comment, button) => {
- if (lastCommentClickedButton.value === button && isCommentPassword.value === comment.commentId) {
- isCommentPassword.value = false; // 비밀번호 창 닫기
+ if (lastCommentClickedButton.value === button && currentPasswordCommentId.value === comment.commentId) {
+ currentPasswordCommentId.value = null; // 비밀번호 창 닫기
+ password.value = '';
+ passwordCommentAlert.value = '';
} else {
- isCommentPassword.value = comment.commentId; // 비밀번호 창 열기
+ currentPasswordCommentId.value = comment.commentId; // 비밀번호 창 열기
+ password.value = '';
+ passwordCommentAlert.value = '';
}
lastCommentClickedButton.value = button;
};
-
-
const togglePassword = (button) => {
if (lastClickedButton.value === button) {
isPassword.value = !isPassword.value;
@@ -556,16 +560,15 @@ const togglePassword = (button) => {
// 게시글 비밀번호 제출
const submitPassword = async () => {
- if (!password.value) {
+ if (!password.value.trim()) {
passwordAlert.value = "비밀번호를 입력해주세요.";
return;
}
- // console.log("📌 요청 시작: submitPassword 실행됨");
try {
const response = await axios.post(`board/${currentBoardId.value}/password`, {
LOCBRDPWD: password.value,
- LOCBRDSEQ: 288, // 나중에 현재 게시글 ID 사용해야함
+ LOCBRDSEQ: 288,
});
if (response.data.code === 200 && response.data.data === true) {
@@ -582,8 +585,6 @@ const submitPassword = async () => {
passwordAlert.value = "비밀번호가 일치하지 않습니다.";
}
} catch (error) {
- // console.log("📌 전체 오류:", error);
-
if (error.response) {
if (error.response.status === 401) {
passwordAlert.value = "비밀번호가 일치하지 않습니다.";
@@ -598,34 +599,40 @@ const submitPassword = async () => {
}
};
-// 댓글 삭제 (비밀번호 확인 후)
+// 댓글 (비밀번호 확인 후)
const submitCommentPassword = async (comment, password) => {
-
- // console.log("비밀번호 :", password);
- // console.log("댓글 ID:", comment.commentId);
-
if (!password) {
passwordCommentAlert.value = "비밀번호를 입력해주세요.";
return;
}
+ const targetComment = findCommentById(comment.commentId, comments.value);
+
try {
- // console.log('서버로 비밀번호 확인 요청')
const response = await axios.post(`board/comment/${comment.commentId}/password`, {
LOCCMTPWD: password,
LOCCMTSEQ: comment.commentId,
});
- // console.log("✅ 서버 응답 데이터:", response.data);
if (response.data.code === 200 && response.data.data === true) {
passwordCommentAlert.value = "";
comment.isCommentPassword = false;
+ // 수정
if (lastCommentClickedButton.value === "edit") {
- comment.isEditTextarea = true;
- passwordCommentAlert.value = "";
- // handleSubmitEdit(comment, comment.content);
+ if (targetComment) {
+ // 다른 모든 댓글의 수정 창 닫기
+ closeAllEditTextareas();
+
+ targetComment.isEditTextarea = true;
+ passwordCommentAlert.value = "";
+ currentPasswordCommentId.value = null;
+
+ } else {
+ alert("수정 취소를 실패했습니다.");
+ }
+ //삭제
} else if (lastCommentClickedButton.value === "delete") {
passwordCommentAlert.value = "";
@@ -633,14 +640,12 @@ const submitCommentPassword = async (comment, password) => {
}
lastCommentClickedButton.value = null;
} else {
- // console.log("❌ 비밀번호가 틀림");
passwordCommentAlert.value = "비밀번호가 일치하지 않습니다.";
}
} catch (error) {
- // console.log("🚨 서버 요청 중 오류 발생:", error.response?.data || error);
- // if (error.response?.status === 401) {
- // console.log("❌ 401 오류: 인증 실패 (비밀번호 불일치)");
- // }
+ if (error.response?.status === 401) {
+ passwordCommentAlert.value = "비밀번호가 일치하지 않습니다";
+ }
passwordCommentAlert.value = "비밀번호가 일치하지 않습니다";
}
};
@@ -672,24 +677,30 @@ const deletePost = async () => {
// 댓글 삭제 (대댓글 포함)
const deleteReplyComment = async (comment) => {
if (!confirm("정말 이 댓글을 삭제하시겠습니까?")) return;
- // console.log("댓글 ID:", comment);
+
+ const targetComment = findCommentById(comment.commentId, comments.value);
+
+ // console.log('잘되니?',comment)
try {
const response = await axios.delete(`board/comment/${comment.commentId}`, {
data: { LOCCMTSEQ: comment.commentId }
});
- // console.log("서버 응답:", response.data);
-
if (response.data.code === 200) {
- // console.log("댓글 삭제 성공!");
await fetchComments();
+
+ if (targetComment) {
+ // console.log('타겟',targetComment)
+ // ✅ 댓글 내용만 "삭제된 댓글입니다."로 변경하고, 구조는 유지
+ targetComment.content = "댓글이 삭제되었습니다.";
+ targetComment.author = "알 수 없음"; // 익명 처리
+ targetComment.isDeleted = true; // ✅ 삭제 상태를 추가
+ }
} else {
- // console.log("댓글 삭제 실패:", response.data.message);
alert("댓글 삭제에 실패했습니다.");
}
} catch (error) {
- // console.log("댓글 삭제 중 오류 발생:", error);
alert("댓글 삭제 중 오류가 발생했습니다.");
}
};
@@ -702,9 +713,6 @@ const handleSubmitEdit = async (comment, editedContent) => {
LOCCMTRPY: editedContent
});
- // 수정 성공 시 업데이트
- // comment.content = editedContent;
- // comment.isEditTextarea = false; f
if (response.status === 200) {
const targetComment = findCommentById(comment.commentId, comments.value);
@@ -712,15 +720,12 @@ const handleSubmitEdit = async (comment, editedContent) => {
targetComment.content = editedContent; // 댓글 내용 업데이트
targetComment.isEditTextarea = false; // 수정 모드 닫기
} else {
- // console.warn("❌ 수정할 댓글을 찾을 수 없음");
alert("수정할 댓글을 찾을 수 없습니다.");
}
} else {
- // console.log("❌ 댓글 수정 실패:", response.data);
alert("댓글 수정 실패했습니다.");
}
} catch (error) {
- // console.error("댓글 수정 중 오류 발생:", error);
alert("댓글 수정 중 오류 발생했습니다.");
}
};
@@ -730,10 +735,8 @@ const handleCancelEdit = (comment) => {
const targetComment = findCommentById(comment.commentId, comments.value);
if (targetComment) {
- // console.log("✅ 원본 데이터 찾음, 수정 취소 처리 가능:", targetComment);
targetComment.isEditTextarea = false;
} else {
- // console.error("❌ 원본 데이터 찾을 수 없음, 수정 취소 실패");
alert("수정 취소를 실패했습니다.");
}
};
@@ -756,7 +759,7 @@ const handleCommentDeleted = (deletedCommentId) => {
return;
}
- // 2대댓글 삭제
+ // 대댓글 삭제
for (let parent of comments.value) {
const childIndex = parent.children.findIndex(child => child.commentId === deletedCommentId);
if (childIndex !== -1) {
@@ -764,11 +767,8 @@ const handleCommentDeleted = (deletedCommentId) => {
return;
}
}
-
- // console.error("❌ 삭제할 댓글을 찾을 수 없음:", deletedCommentId);
};
-
// 날짜
const formattedDate = (dateString) => {
if (!dateString) return "날짜 없음";
diff --git a/src/views/board/BoardWrite.vue b/src/views/board/BoardWrite.vue
index b337d6c..d7b4b43 100644
--- a/src/views/board/BoardWrite.vue
+++ b/src/views/board/BoardWrite.vue
@@ -15,6 +15,8 @@
:is-essential="true"
:is-alert="titleAlert"
v-model="title"
+ @update:alert="titleAlert = $event"
+ @input="validateTitle"
/>
@@ -32,16 +34,19 @@
:id="`category-${index}`"
:value="category.CMNCODVAL"
v-model="categoryValue"
+ @change="categoryAlert = false"
/>
-
카테고리를 선택해주세요.
+
+ 카테고리를 선택해주세요.
+
-
+
+
+
+
첨부파일: {{ fileCount }} / 5개
+
{{ fileError }}
+
+
+ -
+ {{ file.name }}
+ ✖
+
+
+
+
-
@@ -83,23 +104,22 @@
diff --git a/src/views/vacation/VacationManagement.vue b/src/views/vacation/VacationManagement.vue
index f61dba6..c8ac838 100644
--- a/src/views/vacation/VacationManagement.vue
+++ b/src/views/vacation/VacationManagement.vue
@@ -7,9 +7,11 @@