Merge branch 'main' into commuters
This commit is contained in:
commit
495c714b80
@ -1,7 +1,7 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useRoute } from 'vue-router';
|
import router from '@/router';
|
||||||
import { useToastStore } from '@s/toastStore';
|
import { useToastStore } from '@s/toastStore';
|
||||||
import { useLoadingStore } from "@s/loadingStore";
|
import { useLoadingStore } from '@s/loadingStore';
|
||||||
|
|
||||||
const $api = axios.create({
|
const $api = axios.create({
|
||||||
baseURL: import.meta.env.VITE_API_URL,
|
baseURL: import.meta.env.VITE_API_URL,
|
||||||
@ -46,8 +46,8 @@ $api.interceptors.response.use(
|
|||||||
function (error) {
|
function (error) {
|
||||||
const loadingStore = useLoadingStore();
|
const loadingStore = useLoadingStore();
|
||||||
loadingStore.stopLoading();
|
loadingStore.stopLoading();
|
||||||
|
|
||||||
const toastStore = useToastStore();
|
const toastStore = useToastStore();
|
||||||
|
|
||||||
// 오류 응답 처리
|
// 오류 응답 처리
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
switch (error.response.status) {
|
switch (error.response.status) {
|
||||||
@ -55,6 +55,7 @@ $api.interceptors.response.use(
|
|||||||
if (!error.config.headers.isLogin) {
|
if (!error.config.headers.isLogin) {
|
||||||
// toastStore.onToast('인증이 필요합니다.', 'e');
|
// toastStore.onToast('인증이 필요합니다.', 'e');
|
||||||
}
|
}
|
||||||
|
router.push('/login');
|
||||||
break;
|
break;
|
||||||
case 403:
|
case 403:
|
||||||
toastStore.onToast('접근 권한이 없습니다.', 'e');
|
toastStore.onToast('접근 권한이 없습니다.', 'e');
|
||||||
|
|||||||
@ -1,18 +1,20 @@
|
|||||||
/*
|
/*
|
||||||
작성자 : 공현지
|
작성자 : 공현지
|
||||||
작성일 : 2025-01-17
|
작성일 : 2025-01-17
|
||||||
수정자 :
|
수정자 : 박성용
|
||||||
수정일 :
|
수정일 : 2025-03-11
|
||||||
설명 : 공통 스크립트
|
설명 : 공통 스크립트
|
||||||
*/
|
*/
|
||||||
import Quill from 'quill';
|
import Quill from 'quill';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*템플릿 사용법 : $common.변수
|
*템플릿 사용법 : $common.변수
|
||||||
*setup() 사용법 :
|
*setup() 사용법 :
|
||||||
const { appContext } = getCurrentInstance();
|
const { appContext } = getCurrentInstance();
|
||||||
const $common = appContext.config.globalProperties.$common;
|
const $common = appContext.config.globalProperties.$common;
|
||||||
$common.변수
|
or
|
||||||
|
import { inject } from 'vue';
|
||||||
|
const $common = inject('common');
|
||||||
*/
|
*/
|
||||||
const common = {
|
const common = {
|
||||||
// JSON 문자열로 Delta 타입을 변환
|
// JSON 문자열로 Delta 타입을 변환
|
||||||
@ -74,6 +76,12 @@ const common = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 빈값 확인
|
||||||
|
*
|
||||||
|
* @param {} obj
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
isNotEmpty(obj) {
|
isNotEmpty(obj) {
|
||||||
if (obj === null || obj === undefined) return false;
|
if (obj === null || obj === undefined) return false;
|
||||||
if (typeof obj === 'string' && obj.trim() === '') return false;
|
if (typeof obj === 'string' && obj.trim() === '') return false;
|
||||||
@ -103,12 +111,25 @@ const common = {
|
|||||||
/**
|
/**
|
||||||
* 빈 값 확인
|
* 빈 값 확인
|
||||||
*
|
*
|
||||||
* @param {ref} text ex) inNotValidInput(data.value);
|
* @param { ref } text ex) inNotValidInput(data.value);
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
isNotValidInput(text) {
|
isNotValidInput(text) {
|
||||||
return text.trim().length === 0;
|
return text.trim().length === 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 프로필 이미지 반환
|
||||||
|
*
|
||||||
|
* @param { String } profileImg
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getProfileImage(profileImg) {
|
||||||
|
let profileImgUrl = '/img/icons/icon.png'; // 기본 프로필 이미지 경로
|
||||||
|
const UserProfile = `${import.meta.env.VITE_SERVER}upload/img/profile/${profileImg}`;
|
||||||
|
|
||||||
|
return !profileImg || profileImg === '' ? profileImgUrl : UserProfile;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@ -31,17 +31,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, defineProps, defineEmits } from 'vue';
|
import { computed, defineProps, defineEmits, inject } from 'vue';
|
||||||
import DeleteButton from '../button/DeleteBtn.vue';
|
import DeleteButton from '../button/DeleteBtn.vue';
|
||||||
import EditButton from '../button/EditBtn.vue';
|
import EditButton from '../button/EditBtn.vue';
|
||||||
import BoardRecommendBtn from '../button/BoardRecommendBtn.vue';
|
import BoardRecommendBtn from '../button/BoardRecommendBtn.vue';
|
||||||
|
|
||||||
// 기본 프로필 이미지 경로
|
|
||||||
const defaultProfile = '/img/icons/icon.png';
|
|
||||||
|
|
||||||
// 서버의 이미지 경로 (Vue 환경 변수 사용 가능)
|
|
||||||
const baseUrl = import.meta.env.VITE_SERVER; // API 서버 URL
|
|
||||||
|
|
||||||
// Props 정의
|
// Props 정의
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
comment: {
|
comment: {
|
||||||
@ -97,6 +91,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['updateReaction', 'editClick', 'deleteClick']);
|
const emit = defineEmits(['updateReaction', 'editClick', 'deleteClick']);
|
||||||
|
const $common = inject('common');
|
||||||
|
|
||||||
const isDeletedComment = computed(() => {
|
const isDeletedComment = computed(() => {
|
||||||
return props.comment?.content === '삭제된 댓글입니다' && props.comment?.updateAtRaw !== props.comment?.createdAtRaw;
|
return props.comment?.content === '삭제된 댓글입니다' && props.comment?.updateAtRaw !== props.comment?.createdAtRaw;
|
||||||
@ -123,6 +118,6 @@
|
|||||||
|
|
||||||
// 프로필 이미지 경로 설정
|
// 프로필 이미지 경로 설정
|
||||||
const getProfileImage = profileImg => {
|
const getProfileImage = profileImg => {
|
||||||
return profileImg && profileImg != '' ? `${baseUrl}upload/img/profile/${profileImg}` : defaultProfile;
|
return $common.getProfileImage(profileImg);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="">
|
<div class="">
|
||||||
<ul class="row gx-2 mb-2 list-inline">
|
<ul class="row gx-2 mb-0 list-inline">
|
||||||
<li
|
<li
|
||||||
v-for="(user, index) in sortedUserList"
|
v-for="(user, index) in sortedUserList"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -10,7 +10,7 @@
|
|||||||
data-bs-placement="top"
|
data-bs-placement="top"
|
||||||
:aria-label="user.MEMBERSEQ"
|
:aria-label="user.MEMBERSEQ"
|
||||||
>
|
>
|
||||||
<div class="ratio ratio-1x1 mb-2 profile-list">
|
<div class="ratio ratio-1x1 mb-0 profile-list">
|
||||||
<img
|
<img
|
||||||
class="rounded-circle profile-img"
|
class="rounded-circle profile-img"
|
||||||
:src="getUserProfileImage(user.MEMBERPRF)"
|
:src="getUserProfileImage(user.MEMBERPRF)"
|
||||||
|
|||||||
@ -1,43 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container flex-grow-1 container-p-y">
|
<div class="container flex-grow-1 container-p-y">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header d-flex flex-column">
|
||||||
<!-- 검색창 -->
|
<!-- 검색창 -->
|
||||||
<div class="container col-6 mt-12 mb-8">
|
<div class="mb-3 w-100">
|
||||||
<search-bar @update:data="search" @keyup.enter="searchOnEnter"/>
|
<search-bar @update:data="search" @keyup.enter="searchOnEnter" class="flex-grow-1" />
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center" style="gap: 15px;">
|
||||||
|
<!-- 리스트 갯수 선택 -->
|
||||||
|
<select class="form-select w-auto" v-model="selectedSize" @change="handleSizeChange" style="margin-left: 0;">
|
||||||
|
<option value="10">10개씩</option>
|
||||||
|
<option value="20">20개씩</option>
|
||||||
|
<option value="30">30개씩</option>
|
||||||
|
<option value="50">50개씩</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 셀렉트 박스 -->
|
||||||
|
<select class="form-select w-auto" v-model="selectedOrder" @change="handleSortChange">
|
||||||
|
<option value="date">최신날짜</option>
|
||||||
|
<option value="views">조회수</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 공지 접기 기능 -->
|
||||||
|
<div class="form-check mb-0">
|
||||||
|
<input class="form-check-input" type="checkbox" v-model="showNotices" id="hideNotices" />
|
||||||
|
<label class="form-check-label" for="hideNotices">공지 숨기기</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 새 글쓰기 버튼 -->
|
||||||
|
<router-link to="/board/write" class="ms-auto">
|
||||||
|
<WriteButton />
|
||||||
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-datatable">
|
<div class="card-datatable m">
|
||||||
<div class="row mx-6 my-6 justify-content-between g-3 align-items-center">
|
|
||||||
<div class="col-md-6 d-flex flex-column flex-md-row align-items-md-center gap-2 mt-0">
|
|
||||||
<!-- 리스트 갯수 선택 -->
|
|
||||||
<select class="form-select w-25 w-md-100" v-model="selectedSize" @change="handleSizeChange">
|
|
||||||
<option value="10">10개씩</option>
|
|
||||||
<option value="20">20개씩</option>
|
|
||||||
<option value="30">30개씩</option>
|
|
||||||
<option value="50">50개씩</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- 셀렉트 박스 -->
|
|
||||||
<select class="form-select w-25 w-md-100" v-model="selectedOrder" @change="handleSortChange">
|
|
||||||
<option value="date">최신날짜</option>
|
|
||||||
<option value="views">조회수</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- 공지 접기 기능 -->
|
|
||||||
<div class="form-check mb-0 ms-2">
|
|
||||||
<input class="form-check-input" type="checkbox" v-model="showNotices" id="hideNotices" />
|
|
||||||
<label class="form-check-label" for="hideNotices">공지 숨기기</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 d-flex flex-column flex-md-row align-items-md-center justify-content-md-end gap-2">
|
|
||||||
<!-- 새 글쓰기 -->
|
|
||||||
<router-link to="/board/write" class="ms-2">
|
|
||||||
<WriteButton class="btn add-new btn-primary"/>
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 게시판 -->
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="datatables-users table border-top dataTable dtr-column">
|
<table class="datatables-users table border-top dataTable dtr-column">
|
||||||
<thead>
|
<thead>
|
||||||
@ -289,12 +285,11 @@ onMounted(() => {
|
|||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* 댓글 개수 스타일 */
|
.comment-count {
|
||||||
.comment-count {
|
font-size: 0.9rem;
|
||||||
font-size: 0.9rem; /* 글씨 크기 증가 */
|
font-weight: bold;
|
||||||
font-weight: bold;
|
color: #ff5733;
|
||||||
color: #ff5733; /* 강조 색상 (붉은 계열) */
|
border-radius: 4px;
|
||||||
border-radius: 4px; /* 둥근 모서리 */
|
padding: 2px 6px;
|
||||||
padding: 2px 6px; /* 내부 패딩 */
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -514,7 +514,7 @@
|
|||||||
toggleCommentPassword(comment, 'edit');
|
toggleCommentPassword(comment, 'edit');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert('수정이 불가능합니다');
|
toastStore.onToast('수정에 실패하였습니다.', 'e');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -644,7 +644,7 @@
|
|||||||
passwordCommentAlert.value = '';
|
passwordCommentAlert.value = '';
|
||||||
currentPasswordCommentId.value = null;
|
currentPasswordCommentId.value = null;
|
||||||
} else {
|
} else {
|
||||||
alert('수정 취소를 실패했습니다.');
|
toastStore.onToast('수정 취소를 실패하였습니다.', 'e');
|
||||||
}
|
}
|
||||||
//삭제
|
//삭제
|
||||||
} else if (lastCommentClickedButton.value === 'delete') {
|
} else if (lastCommentClickedButton.value === 'delete') {
|
||||||
@ -676,13 +676,15 @@
|
|||||||
toastStore.onToast('게시물이 삭제되었습니다.');
|
toastStore.onToast('게시물이 삭제되었습니다.');
|
||||||
router.push({ name: 'BoardList' });
|
router.push({ name: 'BoardList' });
|
||||||
} else {
|
} else {
|
||||||
alert('삭제 실패: ' + response.data.message);
|
//alert('삭제 실패: ' + response.data.message);
|
||||||
|
toastStore.onToast('삭제 실패: ' + response.data.message, 'e');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
alert(`삭제 실패: ${error.response.data.message || '서버 오류'}`);
|
const errMsg = `삭제 실패: ${error.response.data.message || '서버 오류'}`;
|
||||||
|
toastStore.onToast(errMsg, 'e');
|
||||||
} else {
|
} else {
|
||||||
alert('네트워크 오류가 발생했습니다. 다시 시도해주세요.');
|
toastStore.onToast('네트워크 오류가 발생했습니다. 다시 시도해주세요.', 'e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -710,10 +712,10 @@
|
|||||||
targetComment.isDeleted = true; // ✅ 삭제 상태를 추가
|
targetComment.isDeleted = true; // ✅ 삭제 상태를 추가
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert('댓글 삭제에 실패했습니다.');
|
toastStore.onToast('댓글 삭제에 실패했습니다.', 'e');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert('댓글 삭제 중 오류가 발생했습니다.');
|
toastStore.onToast('댓글 삭제 중 오류가 발생했습니다.', 'e');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -732,13 +734,13 @@
|
|||||||
targetComment.content = editedContent; // 댓글 내용 업데이트
|
targetComment.content = editedContent; // 댓글 내용 업데이트
|
||||||
targetComment.isEditTextarea = false; // 수정 모드 닫기
|
targetComment.isEditTextarea = false; // 수정 모드 닫기
|
||||||
} else {
|
} else {
|
||||||
alert('수정할 댓글을 찾을 수 없습니다.');
|
toastStore.onToast('수정할 댓글을 찾을 수 없습니다.', 'e');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert('댓글 수정 실패했습니다.');
|
toastStore.onToast('댓글 수정 실패했습니다.', 'e');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert('댓글 수정 중 오류 발생했습니다.');
|
toastStore.onToast('댓글 수정 중 오류가 발생하였습니다.', 'e');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -749,7 +751,7 @@
|
|||||||
if (targetComment) {
|
if (targetComment) {
|
||||||
targetComment.isEditTextarea = false;
|
targetComment.isEditTextarea = false;
|
||||||
} else {
|
} else {
|
||||||
alert('수정 취소를 실패했습니다.');
|
toastStore.onToast('수정 취소를 실패했습니다.', 'e');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -796,3 +798,9 @@
|
|||||||
fetchComments();
|
fetchComments();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
.board-content img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user