jichoi / calendar star
최정임 최정임 02-04
250204 최정임 메인 캘린더
@d791728edb958adb163861ce5ba163781a85d76c
 
client/views/component/GoogleCalendar.vue (added)
+++ client/views/component/GoogleCalendar.vue
@@ -0,0 +1,114 @@
+<template>
+    <div>
+      <FullCalendar :options="calendarOptions" />
+    </div>
+  </template>
+  
+  <script>
+  import { ref, onMounted } from 'vue';
+  import FullCalendar from '@fullcalendar/vue3';
+  import dayGridPlugin from '@fullcalendar/daygrid';
+
+
+  
+  // gapi 스크립트 로드 함수
+  function loadGapiScript() {
+    return new Promise((resolve, reject) => {
+      const script = document.createElement('script');
+      script.src = 'https://apis.google.com/js/api.js';
+      script.onload = resolve; // 로드 성공
+      script.onerror = reject; // 로드 실패
+      document.head.appendChild(script);
+    });
+  }
+  
+  export default {
+    name: 'GoogleCalendar',
+    components: {
+      FullCalendar
+    },
+    setup() {
+      const events = ref([]);
+      const calendarOptions = ref({
+        plugins: [dayGridPlugin],
+        initialView: 'dayGridMonth',
+        events: [],
+      titleFormat: { // 월 제목을 원하는 형식으로 변경
+        month: 'long', // 'long' 은 'February' 형식으로
+        year: 'numeric', // '2025' 형식으로
+        day: 'numeric' // '4' 형식으로
+      },
+      locale: 'ko', // 한국어로 설정
+      headerToolbar: {
+        left: 'prev,next today',
+        center: 'title',
+        right: 'dayGridMonth,dayGridWeek,dayGridDay'
+      },
+      buttonText: {
+        today: '오늘',
+        month: '월',
+        week: '주',
+        day: '일'
+      }
+      });
+      
+      const loadClient = () => {
+        if (window.gapi) {
+          window.gapi.client.init({
+            apiKey: 'AIzaSyCNthSbTXgMrCG_dbuIhnk9BEVlp0ME5gM', // 공개 캘린더에 접근하기 위한 API Key
+            discoveryDocs: ['https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest'],
+          }).then(() => {
+            loadCalendarEvents();  // 공개 캘린더 이벤트 불러오기
+          }).catch(error => {
+            console.error('Error initializing gapi client:', error);
+          });
+        } else {
+          console.error('gapi is not defined');
+        }
+      };
+  
+      const loadCalendarEvents = () => {
+        window.gapi.client.calendar.events.list({
+          calendarId: 'ko.south_korea#holiday@group.v.calendar.google.com', // 공개 캘린더 ID
+          timeMin: new Date().toISOString(), // 현재 시점 이후의 이벤트 가져오기
+          showDeleted: false,
+          singleEvents: true,
+          orderBy: 'startTime',
+        }).then((response) => {
+          events.value = response.result.items;
+          calendarOptions.value.events = events.value.map(event => ({
+            title: event.summary,
+            start: event.start.dateTime || event.start.date,
+            end: event.end.dateTime || event.end.date
+          }));
+        }).catch(error => {
+          console.error('Error loading calendar events:', error);
+        });
+      };
+  
+      onMounted(async () => {
+        try {
+          await loadGapiScript();
+          if (window.gapi) {
+            window.gapi.load('client', loadClient);
+          } else {
+            console.error('gapi is not defined after loading script');
+          }
+        } catch (error) {
+          console.error('Error loading gapi script:', error);
+        }
+      });
+  
+      return {
+        calendarOptions
+      };
+    },
+  };
+  </script>
+  
+  <style scoped>
+/* FullCalendar 스타일을 CDN을 통해 불러오기 */
+@import url('https://cdn.jsdelivr.net/npm/@fullcalendar/core@3.10.2/main.css');
+@import url('https://cdn.jsdelivr.net/npm/@fullcalendar/daygrid@3.10.2/main.css');
+</style>
+  (파일 끝에 줄바꿈 문자 없음)
 
client/views/component/ckeditor5/ckeditorComponent.vue (added)
+++ client/views/component/ckeditor5/ckeditorComponent.vue
@@ -0,0 +1,138 @@
+<template>
+    <textarea name="editor5" id="editor5" class="form-control" style="width:100%" ref="editorContainer"></textarea>
+  </template>
+  
+  <script>
+  
+  export default {
+    props: {
+      bbsCn: {
+        type: Object,
+        required: true
+      }
+    },
+    setup(props) {
+      const editorContainer = ref(null);
+      let editor = null;
+  
+      const createEditor = () => {
+        ClassicEditor
+          .create(editorContainer.value, {
+            extraPlugins: [MyCustomUploadAdapterPlugin],
+            removePlugins: ['MediaEmbed', 'MediaEmbedToolbar'],
+            image: {
+              toolbar: [
+                'imageTextAlternative',
+                '|',
+                'imageStyle:alignLeft',
+                'imageStyle:alignCenter',
+                'imageStyle:alignRight',
+                '|',
+                'resizeImage:50',
+                'resizeImage:75',
+                'resizeImage:original',
+                'resizeImage:custom',
+              ],
+              resizeOptions: [
+                {
+                  name: 'resizeImage:original',
+                  value: null,
+                  icon: 'original'
+                },
+                {
+                  name: 'resizeImage:custom',
+                  value: 'custom',
+                  icon: 'custom'
+                },
+                {
+                  name: 'resizeImage:50',
+                  value: '50',
+                  icon: 'medium'
+                },
+                {
+                  name: 'resizeImage:75',
+                  value: '75',
+                  icon: 'large'
+                }
+              ],
+            },
+            fontFamily: {
+              options: [
+                'default',
+                '궁서체',
+                '바탕',
+                '돋움',
+                "Arial, Helvetica, sans-serif",
+                "Courier New, Courier, monospace",
+                "Georgia, serif",
+                "Lucida Sans Unicode, Lucida Grande, sans-serif",
+                "Tahoma, Geneva, sans-serif",
+                "Times New Roman, Times, serif",
+                "Trebuchet MS, Helvetica, sans-serif",
+                "Verdana, Geneva, sans-serif",
+              ],
+            },
+            table: {
+              contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells', 'tableCellProperties'],
+              tableProperties: {
+                defaultProperties: {
+                  borderStyle: 'solid',
+                  borderColor: 'hsl(0, 0%, 0%)',
+                  borderWidth: '1px',
+                }
+              },
+              tableCellProperties: {
+                defaultProperties: {
+                  borderStyle: 'solid',
+                  borderColor: 'hsl(0, 0%, 0%)',
+                  borderWidth: '1px',
+                }
+              }
+            }
+          })
+          .then(editorInstance => {
+            editor = editorInstance;
+            editor.setData(props.bbsCn.bbsCn);
+            editor.model.document.on('change', () => {
+              props.bbsCn.bbsCn = editor.getData();
+            });
+          })
+          .catch(error => {
+            console.error('There was a problem initializing the editor.', error);
+          });
+      };
+  
+      const MyCustomUploadAdapterPlugin = (editor) => {
+        editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
+          return new UploadAdapter(loader);
+        };
+      };
+  
+      onMounted(() => {
+        createEditor(); // Ensure the editor is created when the component is mounted
+      });
+  
+      onBeforeUnmount(() => {  // Changed from beforeDestroy to onBeforeUnmount for Vue 3
+        if (editor) {
+          editor.destroy()
+            .then(() => {
+              editor = null;
+            });
+        }
+      });
+  
+      return {
+        editorContainer,
+      };
+    },
+  }
+  </script>
+  
+  <style scoped>
+  .editor-container {
+    min-height: 300px; /* 에디터 컨테이너의 최소 높이 설정 */
+    border: 1px solid #ccc; /* 에디터 컨테이너의 테두리 설정 */
+  }
+  /* 필요에 따라 스타일 추가 */
+  </style>
+  (파일 끝에 줄바꿈 문자 없음)
client/views/index.js
--- client/views/index.js
+++ client/views/index.js
@@ -1,14 +1,54 @@
-/**
- * @author : 최정우
- * @since : 2022.10.19
- * @dscription : Vue를 활용한 Client단 구현의 시작점(Index) Component 입니다.
- */
 import { createApp } from 'vue';
-import AppRouter from './pages/AppRouter.js';
+import AppRouter from './pages/AppRouter.js';  // AppRouter.js에서 default export한 router를 가져옵니다.
 import App from './pages/App.vue';
-import { createPinia } from 'pinia';
+import Store from "./pages/AppStore.js";
+import VueCookies from 'vue-cookies';  // 쿠키 플러그인 추가
 
-createApp(App)
-  .use(createPinia())
-  .use(AppRouter)
-  .mount('#root');
(파일 끝에 줄바꿈 문자 없음)
+// Google API 스크립트 동적 로드 함수
+function loadGapiScript() {
+  return new Promise((resolve, reject) => {
+    const script = document.createElement('script');
+    script.src = 'https://apis.google.com/js/api.js';
+    script.onload = resolve; // 로드 성공
+    script.onerror = reject; // 로드 실패
+    document.head.appendChild(script);
+  });
+}
+
+// Google API 로드 후 Vue 앱 초기화
+async function initVueApp() {
+  try {
+    // gapi 스크립트를 로드합니다.
+    await loadGapiScript();
+
+    // gapi가 정상적으로 로드되었는지 확인합니다.
+    if (window.gapi) {
+      window.gapi.load('client:auth2', initVue); // Vue 앱 초기화 함수 호출
+    } else {
+      console.error('gapi is not defined');
+    }
+  } catch (error) {
+    console.error("Google API 로드 실패:", error);
+  }
+}
+
+// Vue 앱 초기화 함수
+function initVue() {
+  const router = AppRouter;  // AppRouter를 바로 사용합니다.
+  const vueApp = createApp(App);  // Vue 앱 인스턴스를 생성합니다.
+
+  // Vue Router와 Store 사용
+  vueApp
+    .use(router)  // Vue Router 사용
+    .use(Store)   // Vuex store 사용
+    .use(VueCookies)  // VueCookies 사용
+    .config.globalProperties.$gapi = window.gapi;  // gapi를 전역 속성으로 설정
+
+  // 쿠키 설정
+  vueApp.$cookies.config("1d"); // 쿠키 만료일 설정
+
+  vueApp.mount("#root"); // Vue 앱을 #root에 마운트
+}
+
+// 앱 초기화 함수 실행
+initVueApp();
client/views/pages/AppRouter.js
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
@@ -20,6 +20,7 @@
 import DeptList from '../pages/Manager/DeptList.vue';
 import EmployeeList from '../pages/Manager/EmployeeList.vue';
 import NoticeList from '../pages/Manager/NoticeList.vue';
+import NoticeInsert from '../pages/Manager/NoticeInsert.vue';
 import PubHoliyday from '../pages/Manager/PubHoliyday.vue';
 
 const routes = [
@@ -37,6 +38,7 @@
     { path: '/DeptList', name: 'DeptList', component: DeptList },
     { path: '/EmployeeList', name: 'EmployeeList', component: EmployeeList },
     { path: '/NoticeList', name: 'NoticeList', component: NoticeList },
+    { path: '/NoticeInsert', name: 'NoticeInsert', component: NoticeInsert },
     { path: '/PubHoliyday', name: 'PubHoliyday', component: PubHoliyday },
 ];
 
 
client/views/pages/AppStore.js (added)
+++ client/views/pages/AppStore.js
@@ -0,0 +1,38 @@
+import { createStore } from 'vuex';
+
+const store = createStore({
+  // 애플리케이션의 상태(state)를 정의합니다
+  state: {
+    count: 0,
+  },
+  // 상태를 변경하는 방법을 정의하는 mutations
+  mutations: {
+    increment(state) {
+      state.count++;
+    },
+    decrement(state) {
+      state.count--;
+    },
+  },
+  // 액션을 통해 비동기적으로 상태를 변경하는 방법을 정의하는 actions
+  actions: {
+    incrementAsync({ commit }) {
+      setTimeout(() => {
+        commit('increment');
+      }, 1000);
+    },
+    decrementAsync({ commit }) {
+      setTimeout(() => {
+        commit('decrement');
+      }, 1000);
+    },
+  },
+  // computed 속성과 비슷한 역할을 하는 getters를 정의
+  getters: {
+    getCount(state) {
+      return state.count;
+    },
+  },
+});
+
+export default store;
client/views/pages/Employee/ChuljangList.vue
--- client/views/pages/Employee/ChuljangList.vue
+++ client/views/pages/Employee/ChuljangList.vue
@@ -31,13 +31,13 @@
 
               </div>
               <div class="d-flex justify-content-end ">
-                <button type="button" class="btn btn-outline-secondary" @click="registerLeave">
+                <button type="button" class="btn btn-outline-primary" @click="registerLeave">
                   등록
                 </button>
                 <button type="button" class="btn btn-outline-success" @click="saveChanges">
                   저장
                 </button>
-                <button type="button" class="btn btn-outline-danger" @click="deletePending">
+                <button type="button" class="btn btn-outline-secondary" @click="deletePending">
                   삭제
                 </button>
               </div>
client/views/pages/Employee/HyugaList.vue
--- client/views/pages/Employee/HyugaList.vue
+++ client/views/pages/Employee/HyugaList.vue
@@ -55,7 +55,7 @@
 
               </div>
               <div class="d-flex justify-content-end ">
-                <button type="button" class="btn btn-outline-danger" @click="deletePending">
+                <button type="button" class="btn btn-outline-secondary" @click="deletePending">
                   삭제
                 </button>
               </div>
client/views/pages/Employee/ProjectList.vue
--- client/views/pages/Employee/ProjectList.vue
+++ client/views/pages/Employee/ProjectList.vue
@@ -54,10 +54,10 @@
 
               </div>
               <div class="d-flex justify-content-end ">
-                <button type="button" class="btn btn-outline-success">
-                  수정
+                <button type="button" class="btn btn-outline-primary">
+                  등록
                 </button>
-                <button type="button" class="btn btn-outline-danger" @click="deletePending">
+                <button type="button" class="btn btn-outline-secondary" @click="deletePending">
                   삭제
                 </button>
               </div>
client/views/pages/Manager/DeptList.vue
--- client/views/pages/Manager/DeptList.vue
+++ client/views/pages/Manager/DeptList.vue
@@ -14,13 +14,13 @@
             <div class="d-flex pb-3 justify-content-end ">
 
               <div class="d-flex justify-content-end ">
-                <button type="button" class="btn btn-outline-secondary" @click="registerLeave">
+                <button type="button" class="btn btn-outline-primary" @click="registerLeave">
                   등록
                 </button>
                 <button type="button" class="btn btn-outline-success" @click="saveChanges">
                   저장
                 </button>
-                <button type="button" class="btn btn-outline-danger" @click="deletePending">
+                <button type="button" class="btn btn-outline-secondary" @click="deletePending">
                   삭제
                 </button>
               </div>
 
client/views/pages/Manager/NoticeInsert.vue (added)
+++ client/views/pages/Manager/NoticeInsert.vue
@@ -0,0 +1,351 @@
+<template>
+  <div class="pagetitle">
+    <h1>공지사항 등록</h1>
+  </div>
+  <section class="section">
+    <div class="card">
+      <div class="card-body pt-3">
+        <table class="form-table" style="width: 100%;">
+          <tbody>
+            <tr>
+              <th class="text-lf">
+                <span>제목</span>
+              </th>
+              <td>
+                <input
+                  type="text"
+                  class="form-control"
+                  v-model="notice.title"
+                  placeholder="제목을 입력하세요."
+                />
+              </td>
+            </tr>
+            <tr class="border-top">
+              <th colspan="4" class="text-lf">
+                <span>내용</span>
+              </th>
+            </tr>
+            <tr style="max-height: 600px">
+              <td colspan="4" style="height: 100%" class="pt-0">
+                <ckeditorComponent
+                  ref="ckeditor5"
+                  :bbsCn.sync="bbsCn"
+                ></ckeditorComponent>
+              </td>
+            </tr>
+
+            <!-- 첨부파일 -->
+            <tr class="border-top">
+              <th class="text-lf">
+                첨부파일
+              </th>
+              <td colspan="2">
+                <div class="gd-12 pr0">
+                  <div class="gd-2 pl0 pr0">
+                    <label for="file" class="btn btn-outline-primary">파일찾기</label>
+                    <input
+                      type="file"
+                      id="file"
+                      ref="file"
+                      @change="handleFileInsert"
+                      multiple
+                    />
+                  </div>
+                  <div class="gd-12 pl0 pr0" v-if="fileList.length > 0">
+                    <ul>
+                      <li
+                        v-for="(file, idx) in fileList"
+                        :key="idx"
+                        class="pd-10 mt-10"
+                      >
+                        <div v-if="file.name" class="flex justify-between file-wrap">
+                          <p>{{ file.name }}</p>
+                          <button class="del-btn" @click="handleFileDelete(file, idx)">
+                            X
+                          </button>
+                        </div>
+                      </li>
+                    </ul>
+                  </div>
+                </div>
+              </td>
+            </tr>
+
+            <!-- 공지글 -->
+            <tr class="border-top">
+              <th class="text-lf">
+                공지글
+              </th>
+              <td colspan="3">
+                <div class="d-flex no-gutters">
+                  <div class="col-md-4">
+                    <input
+                      type="radio"
+                      name="notice"
+                      id="notice-y"
+                      class="mr5"
+                      value="Y"
+                      v-model="notice.isNotice"
+                    />
+                    <label for="notice-y">사용</label>
+                  </div>
+                  <div class="col-md-4">
+                    <input
+                      type="radio"
+                      name="notice"
+                      id="notice-n"
+                      class="mr5"
+                      value="N"
+                      v-model="notice.isNotice"
+                    />
+                    <label for="notice-n">미사용</label>
+                  </div>
+                </div>
+              </td>
+            </tr>
+
+            <!-- 공지글 게시기간 -->
+            <tr class="border-top">
+              <th class="text-lf">
+                공지글 게시기간
+              </th>
+              <td colspan="3">
+                <div class="d-flex no-gutters">
+                  <div class="col-md-4">
+                    <input
+                      type="datetime-local"
+                      class="form-control"
+                      v-model="notice.startDate"
+                      @change="checkDateValidity('startDate', $event)"
+                    />
+                  </div>
+                  <div class="pd-1">-</div>
+                  <div class="col-md-4">
+                    <input
+                      type="datetime-local"
+                      class="form-control"
+                      v-model="notice.endDate"
+                      @change="checkDateValidity('endDate', $event)"
+                    />
+                  </div>
+                </div>
+              </td>
+            </tr>
+
+            <!-- 비밀글 -->
+            <tr class="border-top">
+              <th class="text-lf">
+                비밀글
+              </th>
+              <td colspan="3">
+                <div class="d-flex no-gutters">
+                  <div class="col-md-4">
+                    <input
+                      type="radio"
+                      name="private"
+                      id="private-y"
+                      class="mr5"
+                      value="Y"
+                      v-model="notice.isPrivate"
+                    />
+                    <label for="private-y">사용</label>
+                  </div>
+                  <div class="col-md-4">
+                    <input
+                      type="radio"
+                      name="private"
+                      id="private-n"
+                      class="mr5"
+                      value="N"
+                      v-model="notice.isPrivate"
+                    />
+                    <label for="private-n">미사용</label>
+                  </div>
+                </div>
+              </td>
+            </tr>
+          </tbody>
+        </table>
+        <div class="text-end">
+          <button class="btn btn-primary" @click="handleInsert">
+            {{ notice.id == null ? "등록" : "수정" }}
+          </button>
+          <button class="btn btn-secondary" @click="handleCancel">취소</button>
+        </div>
+      </div>
+    </div>
+  </section>
+</template>
+
+<script>
+import ckeditorComponent from "../../component/ckeditor5/ckeditorComponent.vue";
+
+
+
+
+export default {
+  data() {
+    return {
+      notice: {
+        id: null,
+        title: "",
+        isNotice: "Y",
+        startDate: null,
+        endDate: null,
+        isPrivate: "N",
+      },
+      fileList: [],
+    };
+  },
+  methods: {
+    handleCancel() {
+      if (!confirm("등록을 취소하시겠습니까?")) {
+        return;
+      }
+      this.$router.push({ path: "/list.page" });
+    },
+
+    handleFileInsert() {
+      this.fileList = [...this.fileList, ...Array.from(this.$refs.file.files)];
+    },
+
+    handleFileDelete(file, index) {
+      this.fileList.splice(index, 1);
+    },
+
+    handleInsert() {
+      if (!this.validate()) {
+        return;
+      }
+
+      if (this.notice.id == null) {
+        this.saveNotice();
+      } else {
+        this.updateNotice();
+      }
+    },
+
+    saveNotice() {
+      alert("공지사항이 등록되었습니다.");
+      this.$router.push({ path: "/view.page", query: { pageId: this.notice.id } });
+    },
+
+    updateNotice() {
+      alert("공지사항이 수정되었습니다.");
+      this.$router.push({ path: "/view.page", query: { pageId: this.notice.id } });
+    },
+
+    validate() {
+      if (!this.notice.title.trim()) {
+        alert("제목을 입력해주세요.");
+        return false;
+      }
+
+      if (!this.notice.content.trim()) {
+        alert("내용을 입력해주세요.");
+        return false;
+      }
+
+      if (this.notice.isNotice === "Y" && (!this.notice.startDate || !this.notice.endDate)) {
+        alert("공지기간을 올바르게 설정해주세요.");
+        return false;
+      }
+
+      return true;
+    },
+
+    checkDateValidity(field, event) {
+      const val = event.target.value;
+      if (field === "startDate" && this.notice.endDate && this.notice.endDate < val) {
+        alert("시작일은 종료일보다 클 수 없습니다.");
+        this.notice.startDate = null;
+      } else if (field === "endDate" && this.notice.startDate && this.notice.startDate > val) {
+        alert("종료일은 시작일보다 작을 수 없습니다.");
+        this.notice.endDate = null;
+      }
+    },
+     // 에디터 생성
+    createEditor: function () {
+        // ck에디터 적용
+        ClassicEditor
+            .create(document.querySelector('#editor4'), {
+                extraPlugins: [this.MyCustomUploadAdapterPlugin],
+                removePlugins: ['MediaEmbedToolbar'],
+                image: {
+                    toolbar: ['imageTextAlternative', '|', 'imageStyle:alignLeft', 'imageStyle:alignCenter', 'imageStyle:alignRight','|','resizeImage:50','resizeImage:75', 'resizeImage:original','resizeImage:custom',],
+                    resizeOptions: [
+                        {
+                            name: 'resizeImage:original',
+                            value: null,
+                            icon: 'original'
+                        },
+                        {
+                            name: 'resizeImage:custom',
+                            value: 'custom',
+                            icon: 'custom'
+                        },
+                        {
+                            name: 'resizeImage:50',
+                            value: '50',
+                            icon: 'medium'
+                        },
+                        {
+                            name: 'resizeImage:75',
+                            value: '75',
+                            icon: 'large'
+                        }
+                    ],
+                },
+
+            })
+            .then(editor => {
+                this.editor = editor;
+                editor.setData(this.bbsCn.bbsCn);
+                editor.model.document.on('change', () => {
+                    this.bbsCn.bbsCn = editor.getData();
+                });
+            })
+            .catch(error => {
+                console.error('There was a problem initializing the editor.', error);
+            });
+    },
+
+    beforeDestroy: function() {
+        if (this.editor) {
+            this.editor.destroy()
+                .then(() => {
+                    this.editor = null;
+                });
+        }
+    },
+
+    MyCustomUploadAdapterPlugin: function(editor) {
+        editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
+            console.log('loader', loader);
+            return new UploadAdapter(loader);
+        }
+    }
+  },
+  components: {
+    ckeditorComponent,
+  },
+};
+</script>
+
+<style scoped>
+td,
+th {
+  padding: 1rem;
+}
+th {
+  width: 10rem;
+}
+#file {
+  position: absolute;
+  width: 0;
+  height: 0;
+  padding: 0;
+  overflow: hidden;
+  border: 0;
+}
+</style>
client/views/pages/Manager/NoticeList.vue
--- client/views/pages/Manager/NoticeList.vue
+++ client/views/pages/Manager/NoticeList.vue
@@ -1,20 +1,144 @@
 <template>
-    <div class="pagetitle">
-    <h1>공지사항 등록</h1>
+  <div class="pagetitle">
+    <h1>공지사항</h1>
   </div>
+  <!-- End Page Title -->
   <section class="section">
-    <div class="row">ㅇㅇ</div>
-    </section>
-   
-  </template>
-  
-  <script>
-  export default {
-    components: {
+    <div class="row">
+
+
+      <div class="col-lg-12">
+        <div class="card">
+          <div class="card-body">
+            <h5 class="card-title"></h5>
+            <div class="d-flex pb-3 justify-content-between">
+              <div class="datatable-search d-flex gap-1 ">
+                <div class="">
+                  <select class="form-select " v-model="selectedDept">
+                    <option value="" >이름</option>
+                    <option v-for="dept in depts" :key="dept" :value="dept">{{ dept }}</option>
+                  </select>
+                </div>
+                <div class="search-bar d-flex gap-2">
+              <form class="search-form d-flex  align-items-center" method="POST" action="#"
+                @submit.prevent="updateMember">
+                <input type="text" v-model="searchQuery" name="query" placeholder="Search" title="Enter search keyword">
+                <button type="submit" title="Search"><i class="bi bi-search"></i></button>
+              </form>
+            </div>
+                <button type="button" class="btn btn-outline-secondary"
+                  @click="filterData">조회</button>
+
+              </div>
+              <div class="d-flex justify-content-end ">
+                <button type="button" class="btn btn-outline-primary" @click="registerLeave">
+                  등록
+                </button>
+                <!-- <button type="button" class="btn btn-outline-success" @click="saveChanges">
+                  저장
+                </button> -->
+                <button type="button" class="btn btn-outline-secondary" @click="deletePending">
+                  삭제
+                </button>
+              </div>
+
+            </div>
+            <!-- Table  -->
+            <table id="myTable" class="table datatable table-hover">
+              <colgroup>
+                <col width="10%">
+                <col width="75%">
+                <col width="5%">
+                <col width="5%">
+                <col width="5%">
+              </colgroup>
+              <!-- 동적으로 <th> 생성 -->
+              <thead>
+                <tr>
+                  <th>No </th>
+                  <th>제목</th>
+                  <th>작성자</th>
+                  <th>작성일</th>
+                  <th>조회수</th>
+                </tr>
+              </thead>
+              <!-- 동적으로 <td> 생성 -->
+              <tbody>
+                <tr v-for="(item, index) in ChuljangData" :key="item.startDate + index">
+  <td>
+    <div class="form-check">
+      <label class="form-check-label" for="acceptTerms">{{ index + 1 }}</label>
+      <input v-model="item.acceptTerms" class="form-check-input" type="checkbox" />
+    </div>
+  </td>
+  <td><input type="text" v-model="item.theme" /></td>
+  <td><input type="text" v-model="item.name" /></td>
+  <td><input type="text" v-model="item.date" /></td>
+  <td><input type="text" v-model="item.views" /></td>
+</tr>
+              </tbody>
+            </table>
+
+            <!-- End Table -->
+          </div>
+        </div>
+      </div>
+    </div>
+  </section>
+</template>
+
+<script>
+import { DataTable } from 'simple-datatables'
+export default {
+  data() {
+    return {
+      // 데이터 초기화
+      depts: [2023, 2024, 2025], // 연도 목록
+      levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], // 월 목록
+      selectedDept: '',
+      selectedlevel: '',
+      ChuljangData: [
+      { startDate: '', endDate: '', where: '대구', purpose: '',  acceptTerms: false },
+      { startDate: '', endDate: '', where: '경산', acceptTerms: false },
+        // 더 많은 데이터 추가...
+      ],
+      filteredData: [],
+    };
   },
-  }
-  </script>
+  computed: {
+
+  },
+  methods: {
+    registerLeave() {
+      // "NoticeInsert" 페이지로 이동
+      this.$router.push({ path: '/NoticeInsert' });
+    },
+   
+    deletePending() {
+    // 선택된 항목만 필터링하여 삭제
+    const selectedItems = this.ChuljangData.filter(item => item.acceptTerms);
+
+    // 승인된 항목이 없으면 삭제 진행
+    if (selectedItems.length > 0) {
+      this.ChuljangData = this.ChuljangData.filter(item => !item.acceptTerms);
+      alert(`${selectedItems.length}개의 항목이 삭제되었습니다.`);
+    } else {
+      alert("선택된 항목이 없습니다.");
+    }
+  },
+   
+    // 페이지 변경
+    changePage(page) {
+      this.currentPage = page;
+    },
+  },
+  mounted() {
+    
+    // 처음에는 모든 데이터를 표시
+    this.filteredData = this.ChuljangData;
   
-  <style scoped>
-  </style>
-  
(파일 끝에 줄바꿈 문자 없음)
+  },
+};
+</script>
+
+<style scoped></style>
client/views/pages/main/Main.vue
--- client/views/pages/main/Main.vue
+++ client/views/pages/main/Main.vue
@@ -1,8 +1,9 @@
 <template>
-    <div>Main.vue</div>
+    <div> <GoogleCalendar/></div>
 </template>
 
 <script>
+import GoogleCalendar from "../../component/GoogleCalendar.vue"
 
 export default {
     data () {
@@ -19,6 +20,7 @@
 
     },
     components: {
+        GoogleCalendar,
     },
     mounted() {
         console.log('main mounted');
package-lock.json
--- package-lock.json
+++ package-lock.json
@@ -7,21 +7,28 @@
       "dependencies": {
         "@babel/cli": "7.19.3",
         "@babel/core": "7.19.3",
+        "@fullcalendar/core": "^6.1.15",
+        "@fullcalendar/daygrid": "^6.1.15",
+        "@fullcalendar/vue3": "^6.1.15",
         "babel-loader": "8.2.5",
         "css-loader": "6.7.1",
         "express": "4.18.1",
         "file-loader": "6.2.0",
         "fs": "0.0.1-security",
+        "fullcalendar": "^6.1.15",
+        "gapi-script": "^1.2.0",
         "pg": "8.8.0",
         "pinia": "^2.2.0",
         "realgrid": "^2.8.8",
         "simple-datatables": "^9.2.1",
         "url-loader": "4.1.1",
         "vue": "^3.5.13",
+        "vue-cookies": "^1.8.6",
         "vue-loader": "^17.0.0",
         "vue-router": "4.1.5",
         "vue-style-loader": "4.1.3",
         "vue3-sfc-loader": "^0.8.4",
+        "vuex": "^4.1.0",
         "webpack": "5.74.0",
         "webpack-cli": "4.10.0"
       }
@@ -365,6 +372,76 @@
       "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
       "engines": {
         "node": ">=10.0.0"
+      }
+    },
+    "node_modules/@fullcalendar/core": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.15.tgz",
+      "integrity": "sha512-BuX7o6ALpLb84cMw1FCB9/cSgF4JbVO894cjJZ6kP74jzbUZNjtwffwRdA+Id8rrLjT30d/7TrkW90k4zbXB5Q==",
+      "license": "MIT",
+      "dependencies": {
+        "preact": "~10.12.1"
+      }
+    },
+    "node_modules/@fullcalendar/daygrid": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.15.tgz",
+      "integrity": "sha512-j8tL0HhfiVsdtOCLfzK2J0RtSkiad3BYYemwQKq512cx6btz6ZZ2RNc/hVnIxluuWFyvx5sXZwoeTJsFSFTEFA==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@fullcalendar/core": "~6.1.15"
+      }
+    },
+    "node_modules/@fullcalendar/interaction": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.15.tgz",
+      "integrity": "sha512-DOTSkofizM7QItjgu7W68TvKKvN9PSEEvDJceyMbQDvlXHa7pm/WAVtAc6xSDZ9xmB1QramYoWGLHkCYbTW1rQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@fullcalendar/core": "~6.1.15"
+      }
+    },
+    "node_modules/@fullcalendar/list": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/list/-/list-6.1.15.tgz",
+      "integrity": "sha512-U1bce04tYDwkFnuVImJSy2XalYIIQr6YusOWRPM/5ivHcJh67Gm8CIMSWpi3KdRSNKFkqBxLPkfZGBMaOcJYug==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@fullcalendar/core": "~6.1.15"
+      }
+    },
+    "node_modules/@fullcalendar/multimonth": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/multimonth/-/multimonth-6.1.15.tgz",
+      "integrity": "sha512-sEZY6jbOYkeF9TwhUldG+UUVv+hiPlGkS8zZEgPR7ypcjhipyA03c5rPjx7N6huOHqh6lCMH59zlohLooQRlaw==",
+      "license": "MIT",
+      "dependencies": {
+        "@fullcalendar/daygrid": "~6.1.15"
+      },
+      "peerDependencies": {
+        "@fullcalendar/core": "~6.1.15"
+      }
+    },
+    "node_modules/@fullcalendar/timegrid": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.15.tgz",
+      "integrity": "sha512-61ORr3A148RtxQ2FNG7JKvacyA/TEVZ7z6I+3E9Oeu3dqTf6M928bFcpehRTIK6zIA6Yifs7BeWHgOE9dFnpbw==",
+      "license": "MIT",
+      "dependencies": {
+        "@fullcalendar/daygrid": "~6.1.15"
+      },
+      "peerDependencies": {
+        "@fullcalendar/core": "~6.1.15"
+      }
+    },
+    "node_modules/@fullcalendar/vue3": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/vue3/-/vue3-6.1.15.tgz",
+      "integrity": "sha512-ctfTICGrNEIj7gmLHQcUYe0WzDTSW5Vd9hyOnVChxPU75AZU9WqdDMkHwJYnfNxNhT6QQuiMHq/qsRRd5zQwOw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@fullcalendar/core": "~6.1.15",
+        "vue": "^3.0.11"
       }
     },
     "node_modules/@jridgewell/gen-mapping": {
@@ -1655,10 +1732,30 @@
         "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
       }
     },
+    "node_modules/fullcalendar": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/fullcalendar/-/fullcalendar-6.1.15.tgz",
+      "integrity": "sha512-CFnh1yswjRh9puJVDk8VGwTlyZ6eXxr4qLI7QCA0+bozyAm+BluP1US5mOtgk0gEq23nQxGSNDoBvAraz++saQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@fullcalendar/core": "~6.1.15",
+        "@fullcalendar/daygrid": "~6.1.15",
+        "@fullcalendar/interaction": "~6.1.15",
+        "@fullcalendar/list": "~6.1.15",
+        "@fullcalendar/multimonth": "~6.1.15",
+        "@fullcalendar/timegrid": "~6.1.15"
+      }
+    },
     "node_modules/function-bind": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
       "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+    },
+    "node_modules/gapi-script": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gapi-script/-/gapi-script-1.2.0.tgz",
+      "integrity": "sha512-NKTVKiIwFdkO1j1EzcrWu/Pz7gsl1GmBmgh+qhuV2Ytls04W/Eg5aiBL91SCiBM9lU0PMu7p1hTVxhh1rPT5Lw==",
+      "license": "MIT"
     },
     "node_modules/gensync": {
       "version": "1.0.0-beta.2",
@@ -2593,6 +2690,16 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/preact": {
+      "version": "10.12.1",
+      "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz",
+      "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/preact"
+      }
+    },
     "node_modules/proxy-addr": {
       "version": "2.0.7",
       "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -3165,6 +3272,12 @@
         }
       }
     },
+    "node_modules/vue-cookies": {
+      "version": "1.8.6",
+      "resolved": "https://registry.npmjs.org/vue-cookies/-/vue-cookies-1.8.6.tgz",
+      "integrity": "sha512-e2kYaHj1Y/zVsBSM3KWlOoVJ5o3l4QZjytNU7xdCgmkw3521CMUerqHekBGZKXXC1oRxYljBeeOK2SCel6cKuw==",
+      "license": "MIT"
+    },
     "node_modules/vue-loader": {
       "version": "17.0.0",
       "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.0.0.tgz",
@@ -3290,6 +3403,18 @@
       "version": "0.8.4",
       "resolved": "https://registry.npmjs.org/vue3-sfc-loader/-/vue3-sfc-loader-0.8.4.tgz",
       "integrity": "sha512-eziaIrk/N9f9OCpyFEkR6vMsZUHcF5mQslXjffwcb5Iq6EuU74QrlpBeJqA04MvAGT7f5O8la2v9k3NtQnJb3Q=="
+    },
+    "node_modules/vuex": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz",
+      "integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-api": "^6.0.0-beta.11"
+      },
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
     },
     "node_modules/watchpack": {
       "version": "2.4.0",
@@ -3705,6 +3830,54 @@
       "version": "0.5.7",
       "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
       "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="
+    },
+    "@fullcalendar/core": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.15.tgz",
+      "integrity": "sha512-BuX7o6ALpLb84cMw1FCB9/cSgF4JbVO894cjJZ6kP74jzbUZNjtwffwRdA+Id8rrLjT30d/7TrkW90k4zbXB5Q==",
+      "requires": {
+        "preact": "~10.12.1"
+      }
+    },
+    "@fullcalendar/daygrid": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.15.tgz",
+      "integrity": "sha512-j8tL0HhfiVsdtOCLfzK2J0RtSkiad3BYYemwQKq512cx6btz6ZZ2RNc/hVnIxluuWFyvx5sXZwoeTJsFSFTEFA==",
+      "requires": {}
+    },
+    "@fullcalendar/interaction": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.15.tgz",
+      "integrity": "sha512-DOTSkofizM7QItjgu7W68TvKKvN9PSEEvDJceyMbQDvlXHa7pm/WAVtAc6xSDZ9xmB1QramYoWGLHkCYbTW1rQ==",
+      "requires": {}
+    },
+    "@fullcalendar/list": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/list/-/list-6.1.15.tgz",
+      "integrity": "sha512-U1bce04tYDwkFnuVImJSy2XalYIIQr6YusOWRPM/5ivHcJh67Gm8CIMSWpi3KdRSNKFkqBxLPkfZGBMaOcJYug==",
+      "requires": {}
+    },
+    "@fullcalendar/multimonth": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/multimonth/-/multimonth-6.1.15.tgz",
+      "integrity": "sha512-sEZY6jbOYkeF9TwhUldG+UUVv+hiPlGkS8zZEgPR7ypcjhipyA03c5rPjx7N6huOHqh6lCMH59zlohLooQRlaw==",
+      "requires": {
+        "@fullcalendar/daygrid": "~6.1.15"
+      }
+    },
+    "@fullcalendar/timegrid": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.15.tgz",
+      "integrity": "sha512-61ORr3A148RtxQ2FNG7JKvacyA/TEVZ7z6I+3E9Oeu3dqTf6M928bFcpehRTIK6zIA6Yifs7BeWHgOE9dFnpbw==",
+      "requires": {
+        "@fullcalendar/daygrid": "~6.1.15"
+      }
+    },
+    "@fullcalendar/vue3": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/@fullcalendar/vue3/-/vue3-6.1.15.tgz",
+      "integrity": "sha512-ctfTICGrNEIj7gmLHQcUYe0WzDTSW5Vd9hyOnVChxPU75AZU9WqdDMkHwJYnfNxNhT6QQuiMHq/qsRRd5zQwOw==",
+      "requires": {}
     },
     "@jridgewell/gen-mapping": {
       "version": "0.3.2",
@@ -4709,10 +4882,28 @@
       "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
       "optional": true
     },
+    "fullcalendar": {
+      "version": "6.1.15",
+      "resolved": "https://registry.npmjs.org/fullcalendar/-/fullcalendar-6.1.15.tgz",
+      "integrity": "sha512-CFnh1yswjRh9puJVDk8VGwTlyZ6eXxr4qLI7QCA0+bozyAm+BluP1US5mOtgk0gEq23nQxGSNDoBvAraz++saQ==",
+      "requires": {
+        "@fullcalendar/core": "~6.1.15",
+        "@fullcalendar/daygrid": "~6.1.15",
+        "@fullcalendar/interaction": "~6.1.15",
+        "@fullcalendar/list": "~6.1.15",
+        "@fullcalendar/multimonth": "~6.1.15",
+        "@fullcalendar/timegrid": "~6.1.15"
+      }
+    },
     "function-bind": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
       "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+    },
+    "gapi-script": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gapi-script/-/gapi-script-1.2.0.tgz",
+      "integrity": "sha512-NKTVKiIwFdkO1j1EzcrWu/Pz7gsl1GmBmgh+qhuV2Ytls04W/Eg5aiBL91SCiBM9lU0PMu7p1hTVxhh1rPT5Lw=="
     },
     "gensync": {
       "version": "1.0.0-beta.2",
@@ -5340,6 +5531,11 @@
         "xtend": "^4.0.0"
       }
     },
+    "preact": {
+      "version": "10.12.1",
+      "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz",
+      "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg=="
+    },
     "proxy-addr": {
       "version": "2.0.7",
       "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -5719,6 +5915,11 @@
         "@vue/shared": "3.5.13"
       }
     },
+    "vue-cookies": {
+      "version": "1.8.6",
+      "resolved": "https://registry.npmjs.org/vue-cookies/-/vue-cookies-1.8.6.tgz",
+      "integrity": "sha512-e2kYaHj1Y/zVsBSM3KWlOoVJ5o3l4QZjytNU7xdCgmkw3521CMUerqHekBGZKXXC1oRxYljBeeOK2SCel6cKuw=="
+    },
     "vue-loader": {
       "version": "17.0.0",
       "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.0.0.tgz",
@@ -5816,6 +6017,14 @@
       "resolved": "https://registry.npmjs.org/vue3-sfc-loader/-/vue3-sfc-loader-0.8.4.tgz",
       "integrity": "sha512-eziaIrk/N9f9OCpyFEkR6vMsZUHcF5mQslXjffwcb5Iq6EuU74QrlpBeJqA04MvAGT7f5O8la2v9k3NtQnJb3Q=="
     },
+    "vuex": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz",
+      "integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==",
+      "requires": {
+        "@vue/devtools-api": "^6.0.0-beta.11"
+      }
+    },
     "watchpack": {
       "version": "2.4.0",
       "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
package.json
--- package.json
+++ package.json
@@ -2,21 +2,28 @@
   "dependencies": {
     "@babel/cli": "7.19.3",
     "@babel/core": "7.19.3",
+    "@fullcalendar/core": "^6.1.15",
+    "@fullcalendar/daygrid": "^6.1.15",
+    "@fullcalendar/vue3": "^6.1.15",
     "babel-loader": "8.2.5",
     "css-loader": "6.7.1",
     "express": "4.18.1",
     "file-loader": "6.2.0",
     "fs": "0.0.1-security",
+    "fullcalendar": "^6.1.15",
+    "gapi-script": "^1.2.0",
     "pg": "8.8.0",
     "pinia": "^2.2.0",
     "realgrid": "^2.8.8",
     "simple-datatables": "^9.2.1",
     "url-loader": "4.1.1",
     "vue": "^3.5.13",
+    "vue-cookies": "^1.8.6",
     "vue-loader": "^17.0.0",
     "vue-router": "4.1.5",
     "vue-style-loader": "4.1.3",
     "vue3-sfc-loader": "^0.8.4",
+    "vuex": "^4.1.0",
     "webpack": "5.74.0",
     "webpack-cli": "4.10.0"
   },
webpack.config.js
--- webpack.config.js
+++ webpack.config.js
@@ -19,10 +19,12 @@
     }, {
       test: /\.(js|jsx)?$/,
       loader: 'babel-loader',
-    }, {
+    }, 
+    {
       test: /\.css$/,
       use: ['vue-style-loader', 'css-loader']
-    }, {
+    },
+     {
       test: /\.(jpe?g|png|gif|svg|ttf|eot|woff|woff2)$/i,
       use: [{
         loader:'url-loader',
Add a comment
List