결과 화면
개발환경 : 스프링부트 2.7.9 + mybatis +mysql + jsp
fullcalendar 소스는 다음 링크 주소에 있으며 현재 시점 6.15 이다.
https://github.com/fullcalendar/fullcalendar
여기 프로젝에서 적용한 fullcalendar 버전은 5.9.0 이다.
https://github.com/braverokmc79/fullcalendar-5.9.0
MYSQL
CREATE TABLE `tbl_schedulemanage` ( `scheduleId` int(11) NOT NULL AUTO_INCREMENT COMMENT 'schedule번호(PK)', `scheduleName` varchar(255) DEFAULT NULL COMMENT '스케줄제목', `url` varchar(100) DEFAULT NULL COMMENT '링크주소', `textColor` varchar(15) DEFAULT NULL COMMENT '글자색', `color` varchar(15) DEFAULT NULL COMMENT '테두리색', `backgroundColor` varchar(15) DEFAULT NULL COMMENT '배경색', `startDate` varchar(25) DEFAULT NULL COMMENT '시작일', `endDate` varchar(25) DEFAULT NULL COMMENT '끝일', PRIMARY KEY (`scheduleId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Mybatis
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="패키지명.model.dao.mapper.ScheduleManageMapper"> <!-- 관리자 화면일경우 url 생략 --> <select id="selectAdminEventList" resultType="패키지명.model.vo.ScheduleManageVO"> SELECT scheduleid AS "id" , schedulename as "title" , if(textColor is null, '#ffffff', textColor ) as textColor, if(color is null, '#3788d8', color ) as color, if(backgroundColor is null, '#3788d8', backgroundColor ) as backgroundColor , startdate as "start" , enddate AS "end" FROM tbl_schedulemanage </select> <!-- 5년전 데이터까지만 가져오기 --> <select id="selectEventList" resultType="lineage.choco.model.vo.ScheduleManageVO"> SELECT scheduleid AS "id" , schedulename as "title" , if(url is null, '#', url ) as url, if(textColor is null, '#ffffff', textColor ) as textColor, if(color is null, '#3788d8', color ) as color, if(backgroundColor is null, '#3788d8', backgroundColor ) as backgroundColor , startdate as "start" , enddate AS "end" FROM tbl_schedulemanage WHERE startdate >= date_add(now(), interval -5 year) </select> <insert id="addSchedule" parameterType="패키지명.model.vo.ScheduleManageVO"> INSERT INTO tbl_schedulemanage (schedulename, url , textColor, color , backgroundColor , startdate, enddate) VALUES( #{scheduleName}, #{url} , #{textColor} , #{color} , #{backgroundColor} , #{startDate}, #{endDate}) </insert> <select id="getEvent" resultType="패키지명.model.vo.ScheduleManageVO"> SELECT scheduleid AS "id" , schedulename as "title" , if(url is null, '#', url ) as url, if(textColor is null, '#ffffff', textColor ) as textColor, if(color is null, '#3788d8', color ) as color, if(backgroundColor is null, '#3788d8', backgroundColor ) as backgroundColor , startdate as "start" , enddate AS "end" FROM tbl_schedulemanage WHERE scheduleid =#{id} </select> <delete id="deleteSch"> DELETE FROM tbl_schedulemanage WHERE SCHEDULEID =#{id} </delete> <update id="updateSch"> UPDATE tbl_schedulemanage SET <if test="scheduleName!=null and !scheduleName.equals('') "> scheduleName= #{scheduleName}, </if> <if test="url!=null and !url.equals('') "> url=#{url}, </if> <if test="textColor!=null and !textColor.equals('') "> textColor=#{textColor}, </if> <if test="color!=null and !color.equals('') "> color=#{color}, </if> <if test="backgroundColor!=null and !backgroundColor.equals('') "> backgroundColor=#{backgroundColor}, </if> startdate=#{startDate}, enddate=#{endDate} WHERE scheduleid=#{scheduleId} </update> </mapper>
ScheduleManageVO
import lombok.Data; import lombok.ToString; @Data @ToString public class ScheduleManageVO { private int scheduleId; private String scheduleName; private String startDate; private String endDate; private String id; private String title; //스케줄제목 private String url;// 링크주소 private String textColor; //글자색 private String color; //테두리색 private String backgroundColor;//배경색 private String start; //시작일 private String end; //종료일 }
ScheduleManageService
import java.util.List; import java.util.Map; import 패키지.model.vo.ScheduleManageVO; public interface ScheduleManageService { public List<ScheduleManageVO> showSchedule() throws Exception; public int addSchedule(ScheduleManageVO calendarVO) throws Exception; public List<ScheduleManageVO> selectEventList(Map<String, Object> map) throws Exception; public ScheduleManageVO getEvent(Map<String, Object> map) throws Exception; public int deleteSch(ScheduleManageVO calendarVO) throws Exception; public int updateSch(ScheduleManageVO calendarVO) throws Exception; }
ScheduleManageServiceImpl
import java.util.List; import java.util.Map; import org.springframework.stereotype.Service; import 패키지.model.dao.mapper.ScheduleManageMapper; import 패키지.model.vo.ScheduleManageVO; import 패키지.service.ScheduleManageService; import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor public class ScheduleManageServiceImpl implements ScheduleManageService { private final ScheduleManageMapper scheduleManageMapper; public List<ScheduleManageVO> showSchedule() throws Exception { return scheduleManageMapper.showSchedule(); } public int addSchedule(ScheduleManageVO calendarVO) throws Exception { return scheduleManageMapper.addSchedule(calendarVO); } public List<ScheduleManageVO> selectEventList(Map<String, Object> map) throws Exception { return scheduleManageMapper.selectEventList(map); } public ScheduleManageVO getEvent(Map<String, Object> map) throws Exception { return scheduleManageMapper.getEvent(map); } public int deleteSch(ScheduleManageVO calendarVO) throws Exception { return scheduleManageMapper.deleteSch(calendarVO); } public int updateSch(ScheduleManageVO calendarVO) throws Exception { return scheduleManageMapper.updateSch(calendarVO); } }
ScheduleManageMapper
import java.util.List; import java.util.Map; import org.springframework.stereotype.Repository; import 패키지.model.vo.ScheduleManageVO; @Repository public interface ScheduleManageMapper { public List<ScheduleManageVO> showSchedule() throws Exception; public int addSchedule(ScheduleManageVO calendarVO) throws Exception; public List<ScheduleManageVO> selectEventList(Map<String, Object> map) throws Exception; public ScheduleManageVO getEvent(Map<String, Object> map) throws Exception; public int deleteSch(ScheduleManageVO calendarVO) throws Exception; public int updateSch(ScheduleManageVO calendarVO) throws Exception; }
1) 관리자 화면
컨트롤
import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import 패키지.config.auth.PrincipalDetails; import 패키지.model.vo.BoardVO; import 패키지.model.vo.ScheduleManageVO; import 패키지.service.BoardService; import 패키지.service.ScheduleManageService; import lombok.RequiredArgsConstructor; @Controller @RequiredArgsConstructor @RequestMapping("/admin/scheduleManage/") public class AdminScheduleManageController { private final ScheduleManageService calendarService; private final BoardService boardService; /** 스캐쥴 화면 */ @GetMapping("list") public String scheduleManageList(@AuthenticationPrincipal PrincipalDetails priDetails, Model model) throws Exception { model.addAttribute("menuName", "일정관리"); return "admin/scheduleManage/scheduleManage_list"; } @ResponseBody @PostMapping("addSchedule") public int addSchedule(ScheduleManageVO scheduleManageVO) throws Exception { return calendarService.addSchedule(scheduleManageVO); } /** 스캐쥴 목록 가져오기 */ @ResponseBody @PostMapping("selectEventList") public List<ScheduleManageVO> selectEventList(@RequestParam Map<String, Object> map) throws Exception { return calendarService.selectEventList(map); } @ResponseBody @PostMapping("getEvent") public ScheduleManageVO getEvent(@RequestParam Map<String, Object> map) throws Exception { return calendarService.getEvent(map); } @ResponseBody @PostMapping("deleteSch") public int deleteSch(ScheduleManageVO scheduleManageVO) throws Exception { return calendarService.deleteSch(scheduleManageVO); } @ResponseBody @PostMapping("updateSch") public int updateSch(ScheduleManageVO scheduleManageVO) throws Exception { return calendarService.updateSch(scheduleManageVO); } /** 게시판 링크 URL 가져오기 */ @ResponseBody @PostMapping("findBoardTypeByTitleList") public List<BoardVO> findBoardTypeByTitleList(@RequestParam Map<String, Object> map) throws Exception { return boardService.findBoardTypeByTitleList(map); } }
scheduleManage_list.jsp
<%@page import="java.util.List"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html lang="ko"> <head> <!-- 부트스트랩 라이브러리 --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <!-- fullcalendar 라이브러리 --> <link href='https://cdn.jsdelivr.net/gh/braverokmc79/fullcalendar-5.9.0@v5.9.0/lib/main.css' rel='stylesheet' /> <!-- daterangepicker 라이브러리 --> <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.css" /> <!-- 커스텀 .css --> <link href="/resources/lib/fullcalendar/css/scheduleManage_list.css" rel="stylesheet"> </head> <body> <div id='calendar-container'> <div id='calendar'></div> </div> <%@ include file="schedule_manage_modal.jsp" %> <!-- jquery 라이브러리 --> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <!-- 부트스트랩 라이브러리 --> <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous"></script> <!-- 날짜 라이브러리 --> <script type="text/javascript" src="//cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script> <!-- fullcalendar 라이브러리 --> <script src='https://cdn.jsdelivr.net/gh/braverokmc79/fullcalendar-5.9.0@v5.9.0/lib/main.js'></script> <script src='https://cdn.jsdelivr.net/gh/braverokmc79/fullcalendar-5.9.0@v5.9.0/lib/locales-all.min.js'></script> <!-- daterangepicker 라이브러리 --> <script type="text/javascript" src="//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.js"></script> <!-- 커스텀 js --> <script src="/resources/lib/fullcalendar/js/scheduleManage_list.js"></script> </body> </html>
scheduleManage_list.css
@charset "utf-8"; #calendar{ width:80%; margin:20px auto; margin-bottom:200px; } a { color: #000000; text-decoration: none; } /* 드래그 박스의 스타일 */ #external-events { position: fixed; left: 20px; top: 20px; width: 100px; padding: 0 10px; border: 1px solid #ccc; background: #eee; text-align: left; } #external-events h4 { font-size: 16px; margin-top: 0; padding-top: 1em; } #external-events .fc-event { margin: 3px 0; cursor: move; } #external-events p { margin: 1.5em 0; font-size: 11px; color: #666; } #external-events p input { margin: 0; vertical-align: middle; } #calendar-wrap { margin-left: 0px; } #calendar1 { max-width: 1100px; margin: 0 auto; } .fc-myCustomButton-button { /* background: #b31515 !important; */ } .form-group{ margin-bottom: 30px; } .fc-event-time, .fc-event-title{ font-size: 15px; } .empl_nm{ display: none; } .daterangepicker .table-condensed tbody tr td.available:first-child { color: #0d6efd;} .daterangepicker .table-condensed tbody tr td.available:last-child { color: #f95f53 !important;} .daterangepicker td.active, .daterangepicker td.active:hover {background-color: #c5daed; border-color: transparent; color: #fff !important;} .daterangepicker .table-condensed tbody tr td.off { color: #999 !important; opacity:0.5;} .modal .modal-dialog .modal-content .modal-footer { padding: 15px 31px;display: flex;justify-content: space-around;} .fc-daygrid-day.fc-day.fc-day-sat.fc-day-past .fc-daygrid-day-top a , .fc-daygrid-day.fc-day.fc-day-sat.fc-day-future .fc-daygrid-day-top a { color:#2575fc; } /* 토요일 */ .fc-daygrid-day.fc-day.fc-day-sun.fc-day-past .fc-daygrid-day-top a, .fc-daygrid-day.fc-day.fc-day-sun.fc-day-future .fc-daygrid-day-top a {color:#dd3e0e; }/* 일요일 */
scheduleManage_list.js
다음 사이틀 참조해서 googleCalendarApiKey 구글 api 키값 적용
Spring에서 FullCalendar(풀 캘린더)로 Google(구글) 캘린더 DB 연동하기
let $g_arg; //모달창에서 호출하는 함수에서 참조하기 위함) let $calendar; let $daterangeStartDate = ""; let $daterangeEndDate = ""; document.addEventListener('DOMContentLoaded', function() { //FullCalendar 초기 셋팅 및 데이터 불러오기 getFullCalendarEvent(); $('body').on('click', 'button.fc-prev-button', function(e) { console.log("prev1"); }); $('body').on('click', 'button.fc-next-button', function(e) { console.log("next"); }); }); //FullCalendar 초기 셋팅 function getFullCalendarEvent(currentDatePage) { const calendarEl = document.getElementById('calendar'); $calendar = new FullCalendar.Calendar(calendarEl, { googleCalendarApiKey: '구글 APIKEY', //className은 되도록 캘린더랑 맞추길 eventSources: [ { googleCalendarId: 'ko.south_korea#holiday@group.v.calendar.google.com', className: '대한민국의 휴일', color: '#be5683', //rgb,#ffffff 등의 형식으로 할 수 있다 //textColor: 'black' }, ], customButtons: { myCustomButton: { text: '일정입력', click: function(event) { onSelectEvent(event); } } }, headerToolbar: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek,myCustomButton' }, initialDate: currentDatePage, // 초기 날짜 설정 (설정하지 않으면 오늘 날짜가 보인다.) locale: 'ko', // 한국어 설정 editable: true, // 수정 가능 droppable: true, // 드래그 가능 drop: function(arg) { // 드래그 엔 드롭 성공시 }, defaultView: 'timeGridWeek', navLinks: false, // can click day/week names to navigate views allDaySlot: false, eventLimit: true, // allow "more" link when too many events //minTime: '10:00:00', //maxTime: '24:00:00', //contentHeight: 'auto', dateClick: function(arg) { //해당월 페이지 이동을 위한 날짜가져오기 var getData = this.getCurrentData(); var currentDatePage = moment(getData.currentDate).format('YYYY-MM-DD'); $("#currentDatePage").val(currentDatePage); insertModalOpen(arg); }, eventClick: function(info) { //여기서 info 가 아니라 event 로 처리해야 함 event.preventDefault(); //만약 구글 캘린던라면 링크 이동 중단 처리 if (info.event.url.includes('https://www.google.com/calendar/')) { return; } //해당월 페이지 이동을 위한 날짜가져오기 var getData = this.getCurrentData(); var currentDatePage = moment(getData.currentDate).format('YYYY-MM-DD'); $("#currentDatePage").val(currentDatePage); var url = info.event.url; //모달창 호출 updateModalOpen(info.event.id, url); }, eventAdd: function(obj) { // 이벤트가 추가되면 발생하는 이벤트 //console.log(" 이벤트가 추가되면 발생하는 이벤트" ,obj); }, eventChange: function(obj) { // 이벤트가 수정되면 발생하는 이벤트 console.log("1.벤트가 수정되면 발생하는 이벤트 ", obj); const scheduleId = obj.event._def.publicId; const startDate = moment(obj.event._instance.range.start).format(); const endDate = moment(obj.event._instance.range.end).format(); const param = { scheduleId, startDate, endDate } console.log("2.벤트가 수정되면 발생하는 이벤트 ", obj.event._def.url); //만약 구글 캘린던라면 링크 중단 처리 if (obj.event._def.url.includes('https://www.google.com/calendar/')) { alert("지정된 공휴일은 업데이트 처리 될수 없습니다."); getFullCalendarEvent(); return; } $.ajax({ url: "/admin/scheduleManage/updateSch", type: "POST", data: param, dataType: "text", success: function(result) { console.log(" 업데이트 : ", result); }, error: function(result) { console.log("error:"); console.log(result); } }); }, select: function(arg) { // 캘린더에서 드래그로 이벤트를 생성할 수 있다. /* console.log(" 드래그 ", arg) var title = prompt('Event Title:'); if (title) { calendar.addEvent({ title: title, start: arg.start, end: arg.end, allDay: arg.allDay }) } calendar.unselect() */ } }); //End ------------ var calendar = new FullCalendar.Calendar(calendarEl, //DB에서 데이터 가져오기 const arr = getCalendarDataInDB(); $.each(arr, function(index, item) { $calendar.addEvent(item); }); $calendar.render(); $calendar.on('dateClick', function(info) { // console.log('clicked on ' + info.dateStr); }); return $calendar; } //일정 입력 function onSelectEvent() { insertModalOpen("onSelectEvent"); } //arr 는 테스트용으로 DB 에서 스케즐 데이터를 가져오지 못했을 경우 테스트용 데이터 function getCalendarDataInDB() { //arr 임의 데이터 let arr = [{ title: 'evt111', start: '2023-03-22T10:30:00', end: '2023-03-23T10:30:00', backgroundColor: "#52cdff", color: "#000", textColor: "#fff", url: "https://daum.net" }]; $.ajax({ contentType: 'application/json', dataType: 'json', url: '/admin/scheduleManage/selectEventList', type: 'post', async: false, success: function(res) { arr = res; }, error: function(res) { console.log(res); } }); return arr; } // 받은 날짜값을 date 형태로 형변환 해주어야 한다. function convertDate(date) { var date = new Date(date); alert(date.yyyymmdd()); } /* ****************************************************************************************************** 여기서 부터 모달 JS ****************************************************************************************************** * */ function customDaterangePicker() { $('input[name="daterange"]').daterangepicker({ "showDropdowns": true, "showWeekNumbers": true, "showISOWeekNumbers": true, "timePicker": true, "timePicker24Hour": true, "timePickerSeconds": true, "locale": { "format": "YYYY-MM-DD Ah:mm", "separator": " - ", "applyLabel": "적용", "cancelLabel": "취소", "fromLabel": "From", "toLabel": "To", "customRangeLabel": "사용자 지정", "weekLabel": "W", "daysOfWeek": [ "일", "월", "화", "수", "목", "금", "토" ], "monthNames": [ "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월" ], "firstDay": 1 }, "autoUpdateInput": false, "alwaysShowCalendars": true, "startDate": $daterangeStartDate, "endDate": $daterangeEndDate, "minDate": "2000-12-31", "maxDate": "2050-12-31" }, function(start, end, label) { $("#startDate").val(moment(start).format()); $("#endDate").val(moment(end).format()); $("#daterange").val(start.format('YYYY-MM-DD Ah:mm') + " ~ " + end.format('YYYY-MM-DD Ah:mm')); }); } //이벤트 등록 모달 function insertModalOpen(arg) { $(".scheDeleteBtn").css("display", "none"); $(".schInsertBtn").css("display", "block"); $(".schUpdateBtn").css("display", "none"); $(".modal-title").text("일정 등록"); $("#title").val(""); $("#url").val(""); $("#textColor").val("#ffffff"); $("#color").val("#1466b8"); $("#backgroundColor").val("#3788d8"); if (arg == "onSelectEvent") { const date1 = new Date(); arg = { dateStr: moment(date1).format('YYYY MM DD') } } $g_arg = arg; //초기 해당일 날짜 셋팅 $daterangeStartDate = arg.dateStr + " AM12:00"; $daterangeEndDate = arg.dateStr + " PM1:00"; $("#daterange").val($daterangeStartDate + " ~ " + $daterangeEndDate); const sd1 = new Date(arg.dateStr + "T12:00:00"); const ed1 = new Date(arg.dateStr + "T13:00:00"); $("#startDate").val(moment(sd1).format()); $("#endDate").val(moment(ed1).format()); $(".daterangepicker_input input[name=daterangepicker_start]").val($daterangeStartDate); $(".daterangepicker_input input[name=daterangepicker_end]").val($daterangeEndDate); //end - 초기 해당일 날짜 셋팅 customDaterangePicker(); $('.insertModal').modal("show"); } //스케쥴 등록 function insertSch(modal, arg) { const scheduleName = $("#title").val(); const url = $("#url").val(); const textColor = $("#textColor").val(); const color = $("#color").val(); const backgroundColor = $("#backgroundColor").val(); const startDate = $("#startDate").val(); const endDate = $("#endDate").val(); if ($('.insertModal #end').val() <= $('.insertModal #start').val()) { alert('종료시간을 시작시간보다 크게 선택해주세요'); $('.insertModal #end').focus(); return; } if ($('.' + modal + ' #title').val() == '') { alert('제목을 입력해주세요'); $("#title").focus(); return; } if (startDate == "" || startDate == "Invalid date") { alert("시작 날짜를 다시 선택해 주세요."); return; } if (endDate == "" || endDate == "Invalid date") { alert("종료 날짜를 다시 선택해 주세요."); return; } const param = { scheduleName: scheduleName, url: url, textColor: textColor, color: color, backgroundColor: backgroundColor, startDate: startDate, endDate: endDate } //스케줄 작성 $.ajax({ url: "/admin/scheduleManage/addSchedule", type: "POST", data: param, dataType: "text", success: function(result) { //console.log(result); loadEvents(); }, error: function(result) { console.log("error:"); console.log(result); } }); } //캘린더 Refresh function loadEvents() { $calendar.destroy(); const currentDatePage = $("#currentDatePage").val(); //캘린더 데이터 목록 다시 불러오기 getFullCalendarEvent(currentDatePage); $('.insertModal').modal("hide"); } //모달 닫기 function closeModal() { $('.insertModal').modal("hide"); } //이벤트 수정 및 삭제 모달 function updateModalOpen(id, url) { const currentState = "admin"; $(".scheDeleteBtn").css("display", "block"); $(".schInsertBtn").css("display", "none"); $(".schUpdateBtn").css("display", "block"); $(".modal-title").text("일정 수정"); if (currentState == "admin") { $.ajax({ url: "/admin/scheduleManage/getEvent", type: "POST", data: { id: id }, dataType: "json", success: function(data) { const { id, title, url, textColor, color, backgroundColor, start, end } = data; $("#scheduleId").val(id); $("#title").val(title); $("#url").val(data.url); $("#textColor").val(textColor); $("#color").val(color); $("#backgroundColor").val(backgroundColor); //초기 해당일 날짜 셋팅 $("#startDate").val(start); $("#endDate").val(end); $daterangeStartDate = moment(start).format('YYYY-MM-DD Ah:mm'); $daterangeEndDate = moment(end).format('YYYY-MM-DD Ah:mm'); $("#daterange").val($daterangeStartDate + " ~ " + $daterangeEndDate); $(".daterangepicker_input input[name=daterangepicker_start]").val($daterangeStartDate); $(".daterangepicker_input input[name=daterangepicker_end]").val($daterangeEndDate); //end - 해당일 날짜 셋팅 //date picker 호출 customDaterangePicker(); $('.insertModal').modal("show"); }, error: function(result) { console.log("error:"); console.log(result); } }); } else { //구글 이동 //window.open(url); } } //삭제 처리 function deleteSch() { const id = $("#scheduleId").val(); if (confirm("정말 삭제 하시겠습니까?")) { $.ajax({ url: "/admin/scheduleManage/deleteSch", type: "POST", data: { id: id }, dataType: "json", success: function(result) { // console.log(result); if (result == 1) { loadEvents(); } else { console.log("error"); } }, error: function(result) { console.log("error:"); console.log(result); } }); } } //스케쥴 수정 function updateSch(modal, arg) { const scheduleId = $("#scheduleId").val(); const url = $("#url").val(); const textColor = $("#textColor").val(); const color = $("#color").val(); const backgroundColor = $("#backgroundColor").val(); const scheduleName = $("#title").val(); const startDate = $("#startDate").val(); const endDate = $("#endDate").val(); if ($('.insertModal #end').val() <= $('.insertModal #start').val()) { alert('종료시간을 시작시간보다 크게 선택해주세요'); $('.insertModal #end').focus(); return; } if ($('.' + modal + ' #title').val() == '') { alert('제목을 입력해주세요'); $("#title").focus(); return; } if (startDate == "" || startDate == "Invalid date") { alert("시작 날짜를 다시 선택해 주세요."); return; } if (endDate == "" || endDate == "Invalid date") { alert("종료 날짜를 다시 선택해 주세요."); return; } const param = { scheduleId: scheduleId, scheduleName: scheduleName, url: url, textColor: textColor, color: color, backgroundColor: backgroundColor, startDate: startDate, endDate: endDate } $.ajax({ url: "/admin/scheduleManage/updateSch", type: "POST", data: param, dataType: "text", success: function(result) { loadEvents(); }, error: function(result) { console.log("error:"); console.log(result); } }); } /* ****************************************************************************************************** 여기서 부터는 fullcalendar 과 상관없는 게시판 목록 가져오기 ****************************************************************************************************** * */ function findBoardTypeByTitleList(event) { const boardType = event.value; if (boardType) { $.ajax({ type: "POST", url: "/admin/scheduleManage/findBoardTypeByTitleList", data: { boardType }, success: function(res) { console.log(res); const titleList = res.map(board => { console.log(board.agoTime); let agoTime = ""; if (parseInt(board.agoTime) >= 1) { agoTime = "[" + board.agoTime + "일전]" } else { agoTime = "[오늘]"; } let title = ""; if (board.title.length >= 20) { title = board.title.substring(0, 20) + "..."; } else { title = board.title; } title = title + " " + agoTime; return ("<option value='/board/" + boardType + "/read/" + board.bno + "' >" + title + "</option>") }); $("#boardTitleList").html(titleList); setBoardLink(); }, error: function(err) { console.error("에러 : ", err); } }) } } function setBoardLink() { const url = $("#boardTitleList").val(); $('#url').val(url); }
schedule_manage_modal.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <div class="modal fade insertModal" id="myModal"> <div class="modal-dialog"> <div class="modal-content"> <!-- Modal Header --> <div class="modal-header"> <h2 class="modal-title text-center" style="width: 100%; font-size: 1.7rem"></h2> <button type="button" class="close" onclick="closeModal()">×</button> </div> <!-- Modal body --> <div class="modal-body"> <div class="form-group empl_nm"> <label for="empl_nm">scheduleId:</label> <input type="text" class="form-control" id="scheduleId" name="scheduleId" readonly="readonly"> </div> <br> <div class="form-group"> <label for="empl_nm">날짜:</label> <input type="text" name="daterange" id="daterange" class="form-control" /> </div> <div class="form-group empl_nm"> <label for="date">시작일:</label> <input type="text" class="form-control" id="startDate" name="startDate"> </div> <div class="form-group empl_nm"> <label for="date">종료일:</label> <input type="text" class="form-control" id="endDate" name="endDate"> </div> <div class="form-group"> <label for="title">제목:</label> <input type="text" class="form-control" placeholder="" id="title" maxlength="30"> </div> <div class="form-group"> <label for="title">게시판 URL 선택:</label> <select onchange="findBoardTypeByTitleList(this)" class="form-control"> <option value="">게시판을 선택해 주세요.</option> <option value="lineage_news">소식</option> <option value="lineage_stats">서버통계</option> <option value="free">자유게시판</option> <option value="broadcast">방송정보</option> <option value="screen_capture">스크린샷</option> <option value="events">스크린샷</option> </select> <select id="boardTitleList" class="form-control" onchange="setBoardLink()"> </select> </div> <div class="form-group"> <label for="url">url : </label> <input type="url" class="form-control" placeholder="링크주소" name="url" id="url" > </div> <div class="form-group"> <label for="textColor">글자색 : </label> <input type="color" class="form-control" name="textColor" id="textColor" value="#ffffff" > </div> <div class="form-group"> <label for="color">테두리색 : </label> <input type="color" class="form-control" name="color" id="color" value="#1466b8"> </div> <div class="form-group"> <label for="backgroundColor">배경색 : </label> <input type="color" class="form-control" name="backgroundColor" id="backgroundColor" value="#3788d8"> </div> <!-- Modal footer --> <div class="modal-footer text-center"> <button type="button" class="btn btn-danger scheDeleteBtn" onclick="deleteSch()" >삭제</button> <button type="button" class="btn btn-warning schInsertBtn" onclick="insertSch('insertModal', $g_arg)">등록</button> <button type="button" class="btn btn-warning schUpdateBtn" onclick="updateSch('insertModal', $g_arg)" >수정</button> </div> </div> </div> </div> </div>
2) 유저화면
컨트롤
import java.util.List; import java.util.Map; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import 패키지.model.vo.ScheduleManageVO; import 패키지.service.ScheduleManageService; import lombok.RequiredArgsConstructor; @Controller @RequestMapping("/schedule/") @RequiredArgsConstructor public class ScheduleController { private final ScheduleManageService calendarService; /** 목록 */ @GetMapping("list") public String scheduleList( Model model) throws Exception { return "web/schedule/schedule_list"; } @ResponseBody @GetMapping("selectEventList") public List<ScheduleManageVO> selectEventList(@RequestParam Map<String, Object> map) throws Exception { return calendarService.selectEventList(map); } }
schedule_list.jsp
<%@page import="java.util.List"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html lang="ko"> <head> <!-- 부트스트랩 라이브러리 --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <!-- fullcalendar 라이브러리 --> <link href='https://cdn.jsdelivr.net/gh/braverokmc79/fullcalendar-5.9.0@v5.9.0/lib/main.css' rel='stylesheet' /> <!-- daterangepicker 라이브러리 --> <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.css" /> <!-- 커스텀 .css --> <link href="/resources/lib/fullcalendar/css/scheduleManage_list.css" rel="stylesheet"> </head> <body> <div id='calendar-container'> <div id='calendar'></div> </div> <%@ include file="schedule_manage_modal.jsp" %> <!-- jquery 라이브러리 --> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <!-- 부트스트랩 라이브러리 --> <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous"></script> <!-- 날짜 라이브러리 --> <script type="text/javascript" src="//cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script> <!-- fullcalendar 라이브러리 --> <script src='https://cdn.jsdelivr.net/gh/braverokmc79/fullcalendar-5.9.0@v5.9.0/lib/main.js'></script> <script src='https://cdn.jsdelivr.net/gh/braverokmc79/fullcalendar-5.9.0@v5.9.0/lib/locales-all.min.js'></script> <!-- daterangepicker 라이브러리 --> <script type="text/javascript" src="//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.js"></script> <!-- 커스텀 js --> <script src="/resources/lib/fullcalendar/js/schedule_list.js"></script> </body> </html>
schedule_list.js
let $calendar; document.addEventListener('DOMContentLoaded', function() { //FullCalendar 초기 셋팅 및 데이터 불러오기 getFullCalendarEvent(); }); //FullCalendar 초기 셋팅 function getFullCalendarEvent() { const calendarEl = document.getElementById('calendar'); $calendar = new FullCalendar.Calendar(calendarEl, { googleCalendarApiKey: '구글 APIKEY', //className은 되도록 캘린더랑 맞추길 eventSources: [ { googleCalendarId: 'ko.south_korea#holiday@group.v.calendar.google.com', className: '대한민국의 휴일', color: '#be5683', //rgb,#ffffff 등의 형식으로 할 수 있다 //textColor: 'black' }, ], headerToolbar: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek,myCustomButton' }, //initialDate: currentDatePage, // 초기 날짜 설정 (설정하지 않으면 오늘 날짜가 보인다.) locale: 'ko', // 한국어 설정 editable: false, // 수정 가능 droppable: false, // 드래그 가능 drop: function(arg) { // 드래그 엔 드롭 성공시 }, defaultView: 'timeGridWeek', navLinks: false, // can click day/week names to navigate views allDaySlot: false, eventLimit: true, // allow "more" link when too many events dateClick: function(arg) { }, eventClick: function(info) { //여기서 info 가 아니라 event 로 처리해야 함 event.preventDefault(); //만약 구글 캘린던라면 링크 이동 중단 처리 if (info.event.url.includes('https://www.google.com/calendar/')) { return; } const url = info.event.url; if(url!="" || url!="#"){ location.href=url; } }, }); //DB에서 데이터 가져오기 const arr = getCalendarDataInDB(); $.each(arr, function(index, item) { $calendar.addEvent(item); }); $calendar.render(); return $calendar; } //arr 는 테스트용으로 DB 에서 스케즐 데이터를 가져오지 못했을 경우 테스트용 데이터 function getCalendarDataInDB() { //arr 임의 데이터 let arr = [{ title: 'evt111', start: '2023-03-22T10:30:00', end: '2023-03-23T10:30:00', backgroundColor: "#52cdff", color: "#000", textColor: "#fff", url: "https://daum.net" }]; $.ajax({ contentType: 'application/json', dataType: 'json', url: '/schedule/selectEventList', type: 'GET', async: false, success: function(res) { arr = res; }, error: function(res) { console.log(res); } }); return arr; }
댓글 ( 5)
댓글 남기기