휴가,버튼컴포넌트추가가

This commit is contained in:
dyhj625 2025-01-09 10:06:59 +09:00
parent 32fd6897f2
commit 71f6abc61f
13 changed files with 376 additions and 17 deletions

View File

@ -6,8 +6,8 @@
"@/*" : ["src/*"],
"@a/*": ["src/assets/*"],
"@c/*": ["src/components/*"],
"@v/*": ["src/view/*"],
"@l/*": ["src/layout/*"],
"@v/*": ["src/views/*"],
"@l/*": ["src/layouts/*"],
"@s/*": ["src/stores/*"],
"@p/*": ["src/common/plugin/*"],
"@api": ["./src/common/axios-interceptor.js"]

View File

@ -3,13 +3,12 @@
<li>
<BoardProfile profileName=곤데리 :showDetail="false" :author="true" />
<div class="mt-2">저도 궁금합니다.</div>
<button type="button" class="btn btn-text-primary" @click="toggleComment">답변달기</button>
<PlusButton @click="toggleComment"/>
<BoardComentArea v-if="comment" />
<ul class="list-unstyled twoDepth">
<li>
<BoardProfile profileName=곤데리2 :showDetail="false" />
<div class="mt-2">저도 궁금합니다.</div>
<button type="button" class="btn btn-text-primary" @click="toggleComment">답변달기</button>
<BoardComentArea v-if="comment" />
</li>
</ul>
@ -17,13 +16,13 @@
<li>
<BoardProfile profileName=곤데리 :showDetail="false" />
<div class="mt-2">저도 궁금합니다.</div>
<button type="button" class="btn btn-text-primary" @click="toggleComment">답변달기</button>
<PlusButton @click="toggleComment"/>
<BoardComentArea v-if="comment" />
</li>
<li>
<BoardProfile :showDetail="false" :unknown="false" />
<BoardProfile profileName=곤데리 :showDetail="false" />
<div class="mt-2">저도 궁금합니다.</div>
<button type="button" class="btn btn-text-primary" @click="toggleComment">답변달기</button>
<PlusButton @click="toggleComment"/>
<BoardComentArea v-if="comment" />
</li>
</ul>
@ -36,6 +35,7 @@ import BoardProfile from './BoardProfile.vue';
import BoardComentArea from './BoardComentArea.vue';
import { ref, computed } from 'vue';
import Pagination from '../pagination/Pagination.vue';
import PlusButton from '../button/PlusButton.vue';
const comment = ref(false);
@ -63,4 +63,4 @@ const toggleComment = () => {
.btn-text-primary:focus {
background-color: transparent
}
</style>
</style>

View File

@ -24,12 +24,8 @@
</div>
<div class="ms-auto btn-area">
<template v-if="showDetail">
<button class="btn btn-label-primary btn-icon">
<i class='bx bx-edit-alt'></i>
</button>
<button class="btn btn-label-primary btn-icon">
<i class='bx bx-trash' ></i>
</button>
<EditButton />
<DeleteButton />
</template>
<template v-else>
<template v-if="author">
@ -47,6 +43,8 @@
</template>
<script setup>
import DeleteButton from '../button/DeleteButton.vue';
import EditButton from '../button/EditButton.vue';
import BoardRecommendBtn from './BoardRecommendBtn.vue';
defineProps({
@ -85,7 +83,7 @@ defineProps({
}
@media screen and (max-width:450px) {
.btn-area {
.btn-area {
margin-top: 10px;
width: 100%;
}
@ -94,4 +92,4 @@ defineProps({
height: 30px;
}
}
</style>
</style>

View File

@ -0,0 +1,13 @@
<template>
<button class="btn btn-label-primary btn-icon">
<i class='bx bx-trash' ></i>
</button>
</template>
<script>
export default {
name: 'DeleteButton',
methods: {
},
};
</script>

View File

@ -0,0 +1,13 @@
<template>
<button class="btn btn-label-primary btn-icon">
<i class="bx bx-edit-alt"></i>
</button>
</template>
<script>
export default {
name: 'EditButton',
methods: {
},
};
</script>

View File

@ -0,0 +1,13 @@
<template>
<button class="btn btn-label-primary btn-icon">
<i class="icon-base bx bx-plus"></i>
</button>
</template>
<script>
export default {
name: 'PlusButton',
methods: {
},
};
</script>

View File

@ -43,6 +43,12 @@
<div class="text-truncate">Board</div>
</RouterLink>
</li>
<li class="menu-item" :class="$route.path.includes('/vacation') ? 'active' : ''">
<RouterLink class="menu-link" to="/vacation">
<i class="menu-icon tf-icons bx bx-calendar"></i>
<div class="text-truncate">vacation</div>
</RouterLink>
</li>
<li class="menu-item" :class="$route.path.includes('/sample') ? 'active' : ''">
<RouterLink class="menu-link" to="/sample"> <i class="bi "></i>
<i class="menu-icon tf-icons bx bx-calendar"></i>

View File

@ -5,7 +5,7 @@ import BoardWrite from '@v/board/BoardWrite.vue';
const routes = [
{
path: '/',
component: () => import('@v/MainView.vue'),
component: () => import('@/views/vacation/MainView.vue'),
// meta: { requiresAuth: true }
},
{
@ -26,6 +26,10 @@ const routes = [
}
]
},
{
path: '/vacation',
component: () => import('@v/vacation/VacationManagement.vue'),
},
{
path: '/sample',
component: () => import('@c/calendar/SampleCalendar.vue'),

View File

@ -0,0 +1,20 @@
import { ref } from 'vue';
import axios from 'axios';
const events = ref([]);
const fetchEvents = async () => {
const response = await axios.get('/api/calendar/events');
events.value = response.data;
};
const addEvent = async (event) => {
await axios.post('/api/calendar/event', event);
fetchEvents();
};
export default {
events,
fetchEvents,
addEvent,
};

View File

@ -0,0 +1,55 @@
<template>
<div class="modal">
<div class="modal-content">
<h3>휴가 추가</h3>
<input type="text" v-model="title" placeholder="제목" />
<input type="date" v-model="date" />
<button @click="addEvent">추가</button>
<button @click="$emit('close')">닫기</button>
</div>
</div>
</template>
<script>
import calendarStore from '@s/calendarStore';
export default {
data() {
return {
title: '',
date: '',
};
},
methods: {
addEvent() {
if (this.title && this.date) {
calendarStore.addEvent({ title: this.title, start: this.date });
this.$emit('close');
} else {
alert('모든 필드를 입력해주세요.');
}
},
},
};
</script>
<style scoped>
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 5px;
width: 300px;
text-align: center;
}
</style>

View File

@ -0,0 +1,56 @@
<template>
<div class="profile-list">
<div v-for="profile in profiles" :key="profile.id" class="profile">
<img :src="profile.avatar" alt="프로필 사진" class="avatar" />
<div class="info">
<p class="name">{{ profile.name }}</p>
<p class="vacation-count">남은 휴가: {{ profile.remainingVacations }}</p>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
profiles: [
{ id: 1, name: '김철수', avatar: '/avatars/user1.png', remainingVacations: 15 },
{ id: 2, name: '박영희', avatar: '/avatars/user2.png', remainingVacations: 11 },
{ id: 3, name: '이민호', avatar: '/avatars/user3.png', remainingVacations: 10 },
],
};
},
};
</script>
<style scoped>
.profile-list {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.profile {
display: flex;
align-items: center;
border: 1px solid #ddd;
border-radius: 5px;
padding: 10px;
}
.avatar {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 10px;
}
.info {
display: flex;
flex-direction: column;
}
.name {
font-weight: bold;
}
.vacation-count {
color: gray;
}
</style>

View File

@ -0,0 +1,181 @@
<template>
<div class="vacation-management">
<div class="container-xxl flex-grow-1 container-p-y">
<div class="save-button-container">
<button class="btn btn-success" @click="addVacationRequest"></button>
</div>
<div class="card app-calendar-wrapper">
<div class="row g-0">
<div class="col app-calendar-content">
<div class="card shadow-none border-0">
<div class="card-body pb-0">
<full-calendar
ref="fullCalendarRef"
:events="calendarEvents"
:options="calendarOptions"
defaultView="dayGridMonth"
class="flatpickr-calendar-only"
>
</full-calendar>
</div>
</div>
<div class="half-day-buttons">
<button class="btn btn-info" :class="{ active: halfDayType === 'AM' }" @click="toggleHalfDay('AM')"> 오전반차</button>
<button class="btn btn-warning" :class="{ active: halfDayType === 'PM' }" @click="toggleHalfDay('PM')">🌙 오후반차</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import FullCalendar from '@fullcalendar/vue3';
import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';
import 'flatpickr/dist/flatpickr.min.css';
import '@/assets/css/app-calendar.css';
import { reactive, ref } from 'vue';
const fullCalendarRef = ref(null);
const calendarEvents = ref([]);
const selectedDates = ref([]);
const halfDayType = ref(null); // /
const calendarOptions = reactive({
plugins: [dayGridPlugin, interactionPlugin],
initialView: 'dayGridMonth',
headerToolbar: {
left: 'today',
center: 'title',
right: 'prev,next',
},
locale: 'ko',
selectable: true,
dateClick: handleDateClick,
});
function handleDateClick(info) {
const date = info.dateStr;
const dayElement = info.dayEl;
if (!selectedDates.value.includes(date)) {
selectedDates.value.push(date);
if (halfDayType.value === 'AM') {
dayElement.style.backgroundImage = 'linear-gradient(to bottom, #ade3ff 50%, transparent 50%)';
} else if (halfDayType.value === 'PM') {
dayElement.style.backgroundImage = 'linear-gradient(to top, #ade3ff 50%, transparent 50%)';
} else {
dayElement.style.backgroundColor = '#ade3ff';
}
} else {
selectedDates.value = selectedDates.value.filter((d) => d !== date);
dayElement.style.backgroundColor = '';
dayElement.style.backgroundImage = '';
}
halfDayType.value = null; //
}
function toggleHalfDay(type) {
halfDayType.value = halfDayType.value === type ? null : type;
}
function addVacationRequest() {
if (selectedDates.value.length === 0) {
alert('Please select at least one date.');
return;
}
const newEvents = selectedDates.value.map((date) => ({
title: halfDayType.value ? `${halfDayType.value} Half Day Vacation` : 'Vacation',
start: date,
allDay: true,
}));
calendarEvents.value = [...calendarEvents.value, ...newEvents];
alert(`Vacation added for dates: ${selectedDates.value.join(', ')} as ${halfDayType.value || 'Full Day'}`);
selectedDates.value = [];
halfDayType.value = null;
}
</script>
<style scoped>
.vacation-management {
padding: 20px;
}
.save-button-container {
position: fixed;
top: 900px; /* 탑바 아래로 간격 조정 */
right: 400px;
z-index: 1050; /* 탑바보다 높은 값 */
background-color: white;
padding: 10px;
border-radius: 5px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
@media (max-width: 768px) {
.save-button-container {
top: 70px; /* 모바일에서 탑바 아래 간격 조정 */
right: 5px;
left: 5px;
width: calc(100% - 10px); /* 모바일 화면에 맞게 크기 조정 */
text-align: center;
}
}
.half-day-buttons {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 20px;
}
.fc-day-sun .fc-col-header-cell-cushion,
.fc-day-sun a {
color: red;
}
.fc-day-sat .fc-col-header-cell-cushion,
.fc-day-sat a {
color: blue;
}
.flatpickr-calendar-only input.flatpickr-input {
display: none;
}
.flatpickr-input {
display: none;
}
.pt-2.px-3 {
position: relative;
}
.flatpickr-calendar {
position: relative !important;
display: block !important;
width: 100% !important;
height: auto !important;
z-index: 1;
}
.btn-info {
background-color: #17a2b8;
color: white;
}
.btn-warning {
background-color: #ffc107;
color: white;
}
button.active {
opacity: 0.7;
}
</style>