Merge branch 'main' into vacation
This commit is contained in:
commit
9af35ff2d8
@ -819,3 +819,7 @@ input:checked + .slider:before {
|
||||
.font-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -27,10 +27,16 @@
|
||||
<!-- 수정, 삭제 버튼 -->
|
||||
<template v-if="!isDeletedComment && (unknown || isCommentAuthor || isAuthor)">
|
||||
<div class="float-end ms-1">
|
||||
<slot name="gobackBtn"></slot>
|
||||
<EditButton @click.stop="editClick" :is-pushed="isEditPushed" />
|
||||
<DeleteButton :class="'ms-1'" @click.stop="deleteClick" :is-pushed="isDeletePushed" />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="float-end ms-1">
|
||||
<slot name="gobackBtn"></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 좋아요, 싫어요 버튼 (댓글에서만 표시) -->
|
||||
<BoardRecommendBtn
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<div class="row g-0">
|
||||
<div class="col-3 border-end text-center" id="app-calendar-sidebar">
|
||||
<div class="card-body">
|
||||
<img v-if="user" :src="`${baseUrl}upload/img/profile/${user.profile}`" alt="Profile Image" class="w-px-50 h-px-50 rounded-circle" @error="$event.target.src = '/img/icons/icon.png'"/>
|
||||
<img v-if="user" :src="`${baseUrl}upload/img/profile/${user.profile}`" alt="Profile Image" class="w-px-50 h-px-50 rounded-circle object-fit-contain" @error="$event.target.src = '/img/icons/icon.png'"/>
|
||||
<p class="mt-2 fw-bold">
|
||||
{{ user.name }}
|
||||
</p>
|
||||
@ -60,7 +60,7 @@
|
||||
<div class="row my-2 d-flex align-items-center">
|
||||
<div class="col-4">
|
||||
<img :src="`${baseUrl}upload/img/profile/${commuter.profile}`"
|
||||
class="rounded-circle me-2 w-px-50 h-px-50"
|
||||
class="me-2 w-px-50 h-px-50 rounded-circle object-fit-contain"
|
||||
@error="$event.target.src = '/img/icons/icon.png'">
|
||||
|
||||
<span class="fw-bold">{{ commuter.memberName }}</span>
|
||||
@ -399,7 +399,7 @@ const loadCommuters = async () => {
|
||||
// 프로필 이미지 생성
|
||||
const profileImg = document.createElement('img');
|
||||
profileImg.src = `${baseUrl}upload/img/profile/${commuter.profile}`;
|
||||
profileImg.className = 'rounded-circle w-px-20 h-px-20 mx-1 mb-1 position-relative z-5 m-auto';
|
||||
profileImg.className = 'rounded-circle w-px-20 h-px-20 mx-1 mb-1 position-relative z-5 m-auto object-fit-contain';
|
||||
profileImg.style.border = `2px solid ${commuter.projctcolor}`;
|
||||
profileImg.onerror = () => { profileImg.src = '/img/icons/icon.png'; };
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
<img
|
||||
:src="`${baseUrl}upload/img/profile/${commuter.profile}`"
|
||||
alt="User Profile"
|
||||
class="rounded-circle"
|
||||
class="rounded-circle object-fit-contain"
|
||||
:class="isCurrentUser(commuter) ? 'cursor-pointer' : ''"
|
||||
:draggable="isCurrentUser(commuter)"
|
||||
@dragstart="isCurrentUser(commuter) ? dragStart($event, post) : null"
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
:dinnerList="dinnerList"
|
||||
:teaTimeList="teaTimeList"
|
||||
:workShopList="workShopList"
|
||||
@handle-click-vacation="handleClickVacation"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -60,13 +61,14 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { inject, onMounted, reactive, ref, watch, nextTick } from 'vue';
|
||||
import { fetchHolidays } from '@c/calendar/holiday';
|
||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
||||
import { useProjectStore } from '@/stores/useProjectStore';
|
||||
import { useToastStore } from '@s/toastStore';
|
||||
import { useWeatherStore } from '@/stores/useWeatherStore';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import router from '@/router';
|
||||
import FullCalendar from '@fullcalendar/vue3';
|
||||
import dayGridPlugin from '@fullcalendar/daygrid';
|
||||
import interactionPlugin from '@fullcalendar/interaction';
|
||||
@ -342,7 +344,7 @@
|
||||
useFilterEventList(month, day);
|
||||
};
|
||||
|
||||
// 오늘 날짜 노란색 복구
|
||||
// 오늘 날짜 노란색 배경 복구
|
||||
const colorToday = e => {
|
||||
if (todayEL != null && !todayEL.classList.contains('fc-day-today')) todayEL.classList.add('fc-day-today');
|
||||
};
|
||||
@ -504,6 +506,8 @@
|
||||
selectAllow: selectInfo => isSelectableDate(selectInfo.start),
|
||||
dateClick: handleDateClick,
|
||||
dayCellDidMount: arg => {
|
||||
// 날씨 정보 업데이트
|
||||
addWeatherInfo(arg);
|
||||
const dateCell = arg.el;
|
||||
|
||||
// 마우스 홀드시 이벤트 모달
|
||||
@ -534,10 +538,51 @@
|
||||
},
|
||||
});
|
||||
|
||||
// 날짜 정보 업데이트
|
||||
const addWeatherInfo = arg => {
|
||||
const dateStr = $common.dateFormatter(arg.date, 'YMD');
|
||||
// 해당 셀의 날짜와 일치하는 데이터
|
||||
const theDayWeatherInfo = dailyWeatherList.value.find(weather => weather.date === dateStr);
|
||||
|
||||
if (theDayWeatherInfo) {
|
||||
let weatherIconUrl = `https://openweathermap.org/img/wn/${theDayWeatherInfo.icon}.png`;
|
||||
if (theDayWeatherInfo.icon === '01d' || theDayWeatherInfo.icon === '01n') {
|
||||
weatherIconUrl = '/img/icons/sunny-custom.png';
|
||||
}
|
||||
// 날씨 이미지 세팅
|
||||
const weatherEl = document.createElement('img');
|
||||
weatherEl.src = weatherIconUrl;
|
||||
weatherEl.alt = theDayWeatherInfo.description;
|
||||
weatherEl.className = 'weather-icon';
|
||||
weatherEl.style.width = '28px';
|
||||
weatherEl.style.height = '28px';
|
||||
|
||||
// 해당 셀에 이미지 넣기
|
||||
const dayTopEl = arg.el.querySelector('.fc-daygrid-day-top');
|
||||
dayTopEl.classList.add('align-items-center');
|
||||
dayTopEl.prepend(weatherEl); // 이상하게 가장 앞에 넣어야 일자 뒤에 나옴
|
||||
}
|
||||
};
|
||||
|
||||
// 날씨 데이터 변경 감지하여 날씨 정보 업데이트
|
||||
watch(dailyWeatherList, async () => {
|
||||
await nextTick(); // DOM이 업데이트된 후 실행
|
||||
document.querySelectorAll('.fc-daygrid-day').forEach(dayCell => {
|
||||
addWeatherInfo({
|
||||
el: dayCell,
|
||||
date: dayCell.dataset.date,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const handleWheelEvent = e => {
|
||||
handleCloseModal();
|
||||
};
|
||||
|
||||
const handleClickVacation = () => {
|
||||
router.push({ path: 'Vacation' });
|
||||
};
|
||||
|
||||
// 달력 뷰 변경 감지 (월 변경 시 데이터 다시 가져오기)
|
||||
watch(
|
||||
() => fullCalendarRef.value?.getApi().currentData.viewTitle,
|
||||
@ -564,12 +609,6 @@
|
||||
param.append('month', month);
|
||||
param.append('day', day);
|
||||
|
||||
// if (!weatherStore.dailyWeatherList?.length) {
|
||||
// await weatherStore.getWeatherInfo();
|
||||
// //dailyWeatherList.value = weatherStore.dailyWeatherList;
|
||||
// console.log('dailyWeatherList.value: ', dailyWeatherList.value);
|
||||
// }
|
||||
|
||||
// 이벤트 카테고리 호출
|
||||
await fetchCategoryList();
|
||||
await fetchEventList(param);
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
(category.CMNCODVAL === 300205 && teaTimeList?.length) ||
|
||||
(category.CMNCODVAL === 300206 && workShopList?.length)
|
||||
"
|
||||
@click="category.CMNCODVAL == 300202 ? $emit('handleClickVacation') : ''"
|
||||
:class="category.CMNCODVAL == 300202 ? 'pointer' : ''"
|
||||
class="border border-2 mt-3 card p-2"
|
||||
>
|
||||
<div class="row g-2 position-relative">
|
||||
@ -100,6 +102,8 @@
|
||||
type: Array,
|
||||
},
|
||||
});
|
||||
|
||||
defineEmits(['handleClickVacation']);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
:data-bs-original-title="getTooltipTitle(user)"
|
||||
>
|
||||
<img
|
||||
class="rounded-circle user-avatar border border-3"
|
||||
class="user-avatar border border-3 rounded-circle object-fit-contain"
|
||||
:class="{ 'grayscaleImg': isUserDisabled(user) }"
|
||||
:src="`${baseUrl}upload/img/profile/${user.MEMBERPRF}`"
|
||||
:style="`border-color: ${user.usercolor} !important;`"
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</h5>
|
||||
<h5 class="mb-1">{{ data.localVote.LOCVOTTTL }}
|
||||
<h5 class="mb-0">{{ data.localVote.LOCVOTTTL }}
|
||||
<i v-if="yesVotetotal != '0'" class="bx bxs-check-circle link-success"></i>
|
||||
</h5>
|
||||
<small >{{ data.localVote.formatted_LOCVOTRDT }} ~ {{ data.localVote.formatted_LOCVOTEDT }}</small>
|
||||
|
||||
@ -49,7 +49,7 @@
|
||||
@keyup="ValidHandler('title')"
|
||||
/>
|
||||
<div>
|
||||
<QEditor class="" @keyup="ValidHandler('content')" @update:data="handleContentUpdate" @update:imageUrls="imageUrls = $event" :is-alert="wordContentAlert" :initialData="contentValue"/>
|
||||
<QEditor class="q-editor-container" @keyup="ValidHandler('content')" @update:data="handleContentUpdate" @update:imageUrls="imageUrls = $event" :is-alert="wordContentAlert" :initialData="contentValue"/>
|
||||
<div class="text-end mt-5">
|
||||
<button class="btn btn-primary" @click="saveWord" :disabled="titleValue ? !changed : false">
|
||||
<i class="bx bx-check"></i>
|
||||
@ -232,3 +232,11 @@ const handleCategoryFocusout = (value) => {
|
||||
};
|
||||
|
||||
</script>
|
||||
<style>
|
||||
.q-editor-container {
|
||||
max-width: 100%; /* 영역이 넘치지 않게 */
|
||||
overflow: auto; /* 넘치는 내용은 스크롤로 처리 */
|
||||
word-wrap: break-word; /* 긴 단어는 자동으로 줄바꿈 */
|
||||
white-space: normal; /* 내용이 길어지면 자동으로 줄바꿈 */
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -177,7 +177,7 @@
|
||||
v-if="user"
|
||||
:src="`${baseUrl}upload/img/profile/${user.profile}`"
|
||||
alt="Profile Image"
|
||||
class="w-px-40 h-px-40 rounded-circle border border-3"
|
||||
class="w-px-40 h-px-40 rounded-circle border border-3 object-fit-contain"
|
||||
:style="`border-color: ${user.usercolor} !important;`"
|
||||
@error="$event.target.src = '/img/icons/icon.png'"
|
||||
/>
|
||||
|
||||
@ -8,7 +8,7 @@ const routes = [
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: () => import('@v/MainView.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/board',
|
||||
@ -22,6 +22,7 @@ const routes = [
|
||||
},
|
||||
{
|
||||
path: 'write',
|
||||
name: 'BoardWrite',
|
||||
component: () => import('@v/board/BoardWrite.vue'),
|
||||
},
|
||||
{
|
||||
@ -40,12 +41,13 @@ const routes = [
|
||||
path: '/mypage',
|
||||
name: 'MyPage',
|
||||
component: () => import('@v/mypage/MyPage.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/wordDict',
|
||||
name: 'WordDict',
|
||||
component: () => import('@v/wordDict/wordDict.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
@ -67,36 +69,43 @@ const routes = [
|
||||
},
|
||||
{
|
||||
path: '/vacation',
|
||||
name: 'Vacation',
|
||||
component: () => import('@v/vacation/VacationManagement.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/voteboard',
|
||||
name: 'VoteBoard',
|
||||
component: () => import('@v/voteboard/TheVoteBoard.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'VoteBoardList',
|
||||
component: () => import('@v/voteboard/voteBoardList.vue'),
|
||||
},
|
||||
{
|
||||
path: 'write',
|
||||
name: 'VoteboardWrite',
|
||||
component: () => import('@v/voteboard/voteboardWrite.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/projectlist',
|
||||
name: 'Projectlist',
|
||||
component: () => import('@v/projectlist/TheProjectList.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/commuters',
|
||||
name: 'Commuters',
|
||||
component: () => import('@v/commuters/TheCommuters.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/authorization',
|
||||
name: 'Authorization',
|
||||
component: () => import('@v/admin/TheAuthorization.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
|
||||
@ -72,7 +72,6 @@ export const useWeatherStore = defineStore('weather', () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 로컬스토리지 캐시 포함한 로직
|
||||
const getWeatherInfoWithCache = async () => {
|
||||
const now = new Date();
|
||||
const pad = n => String(n).padStart(2, '0');
|
||||
@ -86,17 +85,21 @@ export const useWeatherStore = defineStore('weather', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 캐시 삭제
|
||||
try {
|
||||
const { weather: w, dailyWeatherList: d } = await getWeatherInfo();
|
||||
|
||||
// 기존 캐시 삭제
|
||||
Object.keys(localStorage).forEach(k => {
|
||||
if (k.startsWith('weather_')) localStorage.removeItem(k);
|
||||
});
|
||||
|
||||
try {
|
||||
const { weather: w, dailyWeatherList: d } = await getWeatherInfo();
|
||||
localStorage.setItem(key, JSON.stringify({ weather: w, dailyWeatherList: d }));
|
||||
} catch (e) {
|
||||
// 오류 시 기존 로컬스토리지 값 중 가장 최신 값 사용
|
||||
const oldKey = Object.keys(localStorage).filter(k => k.startsWith('weather_')).sort().pop();
|
||||
console.error('날씨 API 호출 실패, 캐시 fallback 시도 중...');
|
||||
const oldKey = Object.keys(localStorage)
|
||||
.filter(k => k.startsWith('weather_'))
|
||||
.sort()
|
||||
.pop();
|
||||
if (oldKey) {
|
||||
const fallback = JSON.parse(localStorage.getItem(oldKey));
|
||||
weather.value = fallback.weather;
|
||||
|
||||
@ -190,9 +190,9 @@
|
||||
searchText: searchText.value,
|
||||
showNotice: showNotices.value,
|
||||
};
|
||||
|
||||
//localStorage.removeItem
|
||||
// 목록으로 바로 보낼때 필터 유지값
|
||||
//localStorage.setItem(`boardList_${seq}`, JSON.stringify(query));
|
||||
localStorage.setItem(`boardList_${seq}`, JSON.stringify(query));
|
||||
};
|
||||
|
||||
// 스토리지 초기화
|
||||
@ -384,5 +384,4 @@
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@ -21,7 +21,14 @@
|
||||
:is-delete-pushed="isDeletePushed"
|
||||
@editClick="editClick"
|
||||
@deleteClick="deleteClick"
|
||||
/>
|
||||
>
|
||||
<!-- 목록으로 버튼 -->
|
||||
<template #gobackBtn>
|
||||
<button class="btn btn-label-primary btn-icon me-1" @click="goList">
|
||||
<i class="bx bx-left-arrow-alt"></i>
|
||||
</button>
|
||||
</template>
|
||||
</BoardProfile>
|
||||
|
||||
<!-- 비밀번호 입력창 (익명일 경우) -->
|
||||
<div v-if="isPassword && unknown" class="mt-3 w-px-200 ms-auto">
|
||||
@ -451,6 +458,7 @@
|
||||
passwordAlert.value = '';
|
||||
commentAlert.value = '';
|
||||
await fetchComments();
|
||||
activeCommentBtnClass();
|
||||
} else {
|
||||
alert('댓글 작성을 실패했습니다.');
|
||||
}
|
||||
@ -496,6 +504,9 @@
|
||||
const isUnknown = unknown?.unknown ?? false;
|
||||
|
||||
if (isUnknown) {
|
||||
closeAllEditTextareas();
|
||||
closeAllPasswordAreas();
|
||||
activeCommentBtnClass();
|
||||
togglePassword('delete');
|
||||
} else {
|
||||
deletePost();
|
||||
@ -581,6 +592,8 @@
|
||||
|
||||
// 댓글 삭제 버튼 클릭
|
||||
const deleteComment = async comment => {
|
||||
acitveButtonType(); //게시글 버튼 클릭 클래스 제거
|
||||
closeAllEditTextareas();
|
||||
const isMyComment = comment.authorId === currentUserId.value;
|
||||
|
||||
// 익명인 경우
|
||||
@ -686,6 +699,7 @@
|
||||
isEditPushed.value = false;
|
||||
isDeletePushed.value = false;
|
||||
lastClickedButton.value = '';
|
||||
isPassword.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
@ -877,6 +891,16 @@
|
||||
}
|
||||
};
|
||||
|
||||
// 게시글 목록 이동 버튼
|
||||
const goList = () => {
|
||||
// 목록으로 바로 이동시 필터 유지
|
||||
const getFilter = localStorage.getItem(`boardList_${currentBoardId.value}`);
|
||||
router.push({
|
||||
name: 'BoardList',
|
||||
query: getFilter ? JSON.parse(getFilter) : '',
|
||||
});
|
||||
};
|
||||
|
||||
// 댓글 삭제 (대댓글 포함)
|
||||
const handleCommentDeleted = deletedCommentId => {
|
||||
// 댓글 삭제
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
<!-- 단어 목록 -->
|
||||
<ul v-if="total > 0" class="ms-3 list-unstyled" style="overflow-x: hidden; word-wrap: break-word;">
|
||||
<DictCard
|
||||
class="DictCard q-editor-container"
|
||||
v-for="item in wordList"
|
||||
:key="item.WRDDICSEQ"
|
||||
:item="item"
|
||||
@ -330,5 +331,15 @@ import { useRoute } from 'vue-router';
|
||||
top: 5px;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.DictCard {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
.q-editor-container {
|
||||
max-width: 100%; /* 영역이 넘치지 않게 */
|
||||
overflow: auto; /* 넘치는 내용은 스크롤로 처리 */
|
||||
word-wrap: break-word; /* 긴 단어는 자동으로 줄바꿈 */
|
||||
white-space: normal; /* 내용이 길어지면 자동으로 줄바꿈 */
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user