jichoi / lms_front star
구자현 구자현 2024-08-13
240813 Dashboard 수정(이후 수정 필요)
@679317d6d97638f787b1c8c55c6ada1833ec6a21
client/resources/css/style.css
--- client/resources/css/style.css
+++ client/resources/css/style.css
@@ -284,7 +284,7 @@
 .mypage .photobook .title1{font-family: 'ONEMobilePOPOTF'; font-weight: 100;}
 .mypage .textbook .text {border-radius:  12px 12px 0px 0px; height: 68px;
 }
-.mypage .textbook .box{height: 199px; text-align: center; padding: 10px;}
+.mypage .textbook .box{height: fit-content; text-align: center; padding: 10px;}
 .mypage .book-red .text{background-color: #DB2B39;}
 /* .mypage .textbook:nth-child(2) .text */
 .mypage .book-blue .text{background-color:#2D7DD2;}
@@ -674,7 +674,7 @@
 .listenGroup img.bg {
   position: absolute;
   top: 0;
-  width: 1670px;
+  width: 100%;
   height: 710px;
 }
 
@@ -690,14 +690,23 @@
   top: 45%;
     left: 31%;
   transform: translate(-50%, -50%);
-  width: 900px;
+  /* width: 900px; */
+}
+.dropGroup .textbox {
+  position: absolute;
+  top: 45%;
+    left: 53%;
+  transform: translate(-50%, -50%);
+  /* width: 900px; */
 }
 
 .listenGroup .textbox p {
   font-size: 28px;
   line-height: 45px;
+  width: 100%;
+  
 }
-
+p.textbox {width: 100%;}
 .listenGroup p.title {
   font-size: 42px;
   text-shadow: 0px 0px 0 #fff, 0px 0px 0 #fff, 0px 0px 0 #fff, 0px 0px 0 #fff;
@@ -744,7 +753,7 @@
 
 /* step2_2 */
 .popTxt{position: relative; }
-.popTxt img:last-child{position: absolute; top: 0; left: 0;}
+
 
 h4{font-size: 20px;}
 
 
client/resources/img/jumpingRabbit.gif (Binary) (added)
+++ client/resources/img/jumpingRabbit.gif
Binary file is not shown
 
client/views/component/TextToImage.vue (added)
+++ client/views/component/TextToImage.vue
@@ -0,0 +1,244 @@
+<template>
+    <div class="flex justify-center align-center" style="margin-top : 30px">
+        <div class="content title-box aibox">
+            <p class="title mt25 title-bg">모르는 단어를 물어봐~!</p>
+            <div class="propmt-container">
+                <div class="input-conatiner">
+                    <input v-model="word" @keyup.enter="startProgress(word)"/>
+                    <button @click="startProgress(word)"> <p> 생성하기 </p> </button>
+                </div>
+                <div class="progress-container">
+                    <div class="progress-bar" :style="{ width: progressWidth + '%' }">{{ Math.floor(progressWidth) }}%</div>
+                    
+                    <!-- 로딩 이미지 -->
+                    <div v-if="progressWidth < 100 && progressWidth > 1" class="loading-container">
+                        <img src="../../resources/img/jumpingRabbit.gif" alt="Loading" class="loading-gif"/>
+                        <p> 잠깐만 기다려봐~ </p>
+                    </div>
+
+                </div>
+                <div v-if="visibleWord" class="result-container">
+                    <img :src="imageSrc"/>
+                    <div class="word-container">
+                        <h2> {{ inputWord }}  </h2>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    
+</template>
+
+<script>
+import axios from 'axios';
+
+export default {
+    data() {
+        return {
+            progressWidth: 0,
+            inputWord : "",
+
+            // 하드 코딩 -> 수정 필요 : API
+            word : "",
+            imageSrc : "",
+            imageData: null, // 서버에서 받아온 이미지 데이터 저장
+
+            visibleWord : false,
+        };
+    },
+    mounted() {
+        
+    },
+    methods: {
+        // 상태 진행 바(progress bar)
+        startProgress(word) {
+            if (this.progressWidth == 100) {
+                this.resetProgress();
+            }
+
+            if (this.progressWidth > 0) return; // 이미 진행 중이면 중복 실행 방지
+
+            this.setWord(word);
+
+            const interval = 300; // 30ms
+            const totalDuration = 3000; // 총 시간 : 10초
+            const steps = totalDuration / interval;
+            const increment = 100 / steps;
+
+            const progressInterval = setInterval(() => {
+                this.progressWidth += increment;
+                if (this.progressWidth >= 100) {
+                    this.progressWidth = 100;
+                    clearInterval(progressInterval);
+                    this.visibleWord = true;
+
+                     // 진행이 완료된 후에 이미지 데이터 렌더링
+                    if (this.imageData) {
+                        this.imageSrc = this.imageUrl;
+                    }
+                }
+            }, interval);
+
+            this.getAiImage();
+        },
+        setWord(word) {
+            if (this.progressWidth > 0 && this.progressWidth < 100) return; // progressWidth가 0 또는 100이 아니면 실행 중지
+            this.inputWord = word;
+            this.visibleWord = false;
+        },
+        
+        resetProgress() {
+            this.progressWidth = 0;
+            this.visibleWord = false;
+            this.imageSrc = ""; // 이미지 URL 초기화
+            this.imageData = null; // 이미지 데이터 초기화
+        },
+
+        // 이미지 API
+        async getAiImage(){
+            const url = "http://takensoftai.iptime.org:20200/generate_json";
+            // console.log(`word : ${this.word}`);
+            try {
+                const response = await axios({
+                    url: url,
+                    method: "post",
+                    headers: {
+                        "Content-Type": "application/json; charset=UTF-8",
+                    },
+                    responseType: 'arraybuffer',
+                    data: {
+                        text: this.word
+                    },
+                });
+                
+                // console.log(`응답 : ${response}`);
+
+                // 바이너리 데이터 -> Blob으로 변환
+                const blob = new Blob([response.data], { type: 'image/png' });
+
+                // Blob에서 객체 URL 생성
+                const imageUrl = URL.createObjectURL(blob);
+
+                // 이미지 URL 설정
+                this.imageSrc = imageUrl;
+            } catch (err) {
+                console.log(err);
+            }
+        }
+    }
+}
+</script>
+
+<style>
+.propmt-container{
+    padding: 0px 50px 30px 50px;
+}
+/* 입력 컨테이너  */
+.input-conatiner{
+    display: flex;
+    align-items: center;
+    gap: 30px;
+    height: 50px;
+}
+.input-conatiner input{
+    width: 90%;
+    height: 100%;
+    padding: 10px 30px;
+    border: none;
+    border-radius: 10px;
+    box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;
+    outline: none;
+    font-size : 28px;
+}
+.input-conatiner button{
+    width: 15%;
+    height: 100%;
+    border: none;
+    border-radius: 10px;
+    cursor: pointer;
+    background-color: #ffba08;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+
+.input-conatiner button p{
+    font-size : 28px;
+}
+
+/* 진행 상태바 */
+.progress-container {
+    width: 100%;
+    background-color: #ffffff;
+    border-radius: 5px;
+    overflow: hidden;
+    margin-top: 20px;
+    display : flex;
+    flex-direction: column;
+    gap :30px;
+}
+
+.progress-bar {
+    height: 30px;
+    width: 0;
+    background-color: #4caf50;
+    text-align: center;
+    line-height: 30px;
+    color: white;
+    border : none;
+    border-radius: 5px;
+    transition: width 0.3s;
+    font-size : 20px;
+}
+
+/* 로딩 gif */
+.loading-container{
+    display : flex;
+    flex-direction: column;
+    align-items: center;
+    gap: 30px;
+    text-align: center;
+    margin-top : 30px;
+    padding-bottom : 40px;
+}
+.loading-gif{
+    width: 25%;
+    border-radius: 500px;
+}
+.loading-container p {
+    font-size : 25px;
+}
+
+/* 결과 container */
+.result-container{
+    margin-top: 30px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    gap: 30px;
+}
+.result-container img{
+    width : 30%;
+}
+.word-container{
+    width: 30%;
+    text-align: center;
+    padding: 20px 0px;
+    border: 3px solid #4caf50;
+    border-radius: 8px;
+    display: flex;
+    flex-direction: column;
+    gap: 15px;
+}
+.word-container h2 {
+    font-size : 28px;
+}
+
+.aibox{
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    height: auto;
+    padding: 30px 0px;
+}
+</style>
client/views/pages/main/Chapter/Chapter1.vue
--- client/views/pages/main/Chapter/Chapter1.vue
+++ client/views/pages/main/Chapter/Chapter1.vue
@@ -17,10 +17,12 @@
             </div>
             <div class="next-btn"  @click="goToPage('Chapter1_1')"><img src="../../../../resources/img/right.png" alt=""></div>
         </div>
+        <TextToImage/>
     </div>
 </template>
 
 <script>
+import TextToImage from '../../../component/TextToImage.vue';
 export default {
     data() {
         return {
@@ -38,6 +40,7 @@
 
     },
     components: {
+        TextToImage : TextToImage,
     },
     mounted() {
     }
client/views/pages/main/Chapter/Chapter1_2.vue
--- client/views/pages/main/Chapter/Chapter1_2.vue
+++ client/views/pages/main/Chapter/Chapter1_2.vue
@@ -49,10 +49,12 @@
       </div>
       <div class="next-btn" @click="goToPage('Chapter1_3')"><img src="../../../../resources/img/right.png" alt=""></div>
     </div>
+    <TextToImage/>
   </div>
 </template>
 
 <script>
+import TextToImage from '../../../component/TextToImage.vue';
 export default {
   data() {
     return {
@@ -70,6 +72,7 @@
 
   },
   components: {
+    TextToImage : TextToImage
   },
   mounted() {
     const textArea = this.$refs.textArea;
client/views/pages/main/Chapter/Chapter1_3.vue
--- client/views/pages/main/Chapter/Chapter1_3.vue
+++ client/views/pages/main/Chapter/Chapter1_3.vue
@@ -20,10 +20,12 @@
             </div>
             <div class="next-btn"  @click="goToPage('Dashboard')"><img src="../../../../resources/img/right.png" alt=""></div>
         </div>
+        <TextToImage/>
     </div>
 </template>
 
 <script>
+import TextToImage from '../../../component/TextToImage.vue';
 export default {
     data() {
         return {
@@ -41,8 +43,12 @@
 
     },
     components: {
+        TextToImage : TextToImage
     },
     mounted() {
     }
 }
-</script>
(No newline at end of file)
+</script>
+<style scoped>
+.listenGroup .textbox { width: 900px};
+</style>
(No newline at end of file)
client/views/pages/main/Chapter/Chapter2_2.vue
--- client/views/pages/main/Chapter/Chapter2_2.vue
+++ client/views/pages/main/Chapter/Chapter2_2.vue
@@ -1,31 +1,40 @@
 <template>
-  <div id="Chapter2_2" class="content-wrap">
-     <div class="title-box mb25 flex align-center mt40">
-        <span class="title mr40">1. Hello WORLD</span>
-        <span class="subtitle">my name is dd</span>
-     </div>
-     <div class="flex justify-between align-center">
-        <div class="pre-btn" @click="goToPage('Chapter2_1')"><img src="../../../../resources/img/left.png" alt=""></div>
-        <div class="content title-box">
-           <p class="title mt25 title-bg">step2</p>
-          <div class="flex align-center mb30">
-          <p class="subtitle2 mr20">카드를 뒤집어 보세요.</p>
-          <!-- <button><img src="../../../../resources/img/btn10_s.png" alt="">
+   <div id="Chapter2_2" class="content-wrap">
+      <div class="title-box mb25 flex align-center mt40">
+         <span class="title mr40">1. Hello WORLD</span>
+         <span class="subtitle">my name is dd</span>
+      </div>
+      <div class="flex justify-between align-center">
+         <div class="pre-btn" @click="goToPage('Chapter2_1')"><img src="../../../../resources/img/left.png" alt="">
+         </div>
+         <div class="content title-box">
+            <p class="title mt25 title-bg">step2</p>
+            <div class="flex align-center mb30">
+               <p class="subtitle2 mr20">카드를 뒤집어 보세요.</p>
+               <!-- <button><img src="../../../../resources/img/btn10_s.png" alt="">
           </button> -->
-        </div>
+            </div>
 
-           <div class="imgGroup">
-              <div class="flex justify-center" style="gap: 90px;">
-                 <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)">
-                    <img :src="item.imgSrc1" >
-                    <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
-                 </button>
-              </div>
-           </div>
-        </div>
-        <div class="next-btn" @click="goToPage('Chapter2_3')"><img src="../../../../resources/img/right.png" alt=""></div>
-     </div>
-  </div>
+            <div class="imgGroup">
+               <div class="flex justify-center" style="gap: 90px;">
+                  <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)">
+                     <div class="listenGroup">
+                        <img :src="item.imgSrc1">
+                        <p class="textbox"><img :src="item.imgSrc"></p>
+
+                     </div>
+                     <div class="listenGroup" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
+                        <img :src="item.imgSrc2">
+                        <p class="title4 textbox">{{ item.title }}</p>
+                     </div>
+                  </button>
+               </div>
+            </div>
+         </div>
+         <div class="next-btn" @click="goToPage('Chapter2_3')"><img src="../../../../resources/img/right.png" alt="">
+         </div>
+      </div>
+   </div>
 </template>
 
 
@@ -34,10 +43,10 @@
    data() {
       return {
          items: [
-            { imgSrc1: 'client/resources/img/img49_s_1.png', imgSrc2: 'client/resources/img/img50_s_2.png', isSecondImageVisible: false },
-            { imgSrc1: 'client/resources/img/img49_s_2.png', imgSrc2: 'client/resources/img/img50_s_3.png', isSecondImageVisible: false },
-            { imgSrc1: 'client/resources/img/img49_s_3.png', imgSrc2: 'client/resources/img/img50_s_4.png', isSecondImageVisible: false },
-            { imgSrc1: 'client/resources/img/img49_s_4.png', imgSrc2: 'client/resources/img/img50_s_1.png', isSecondImageVisible: false }
+            { imgSrc1: 'client/resources/img/img49_s.png', imgSrc2: 'client/resources/img/img50_s.png', imgSrc: 'client/resources/img/img59_36s.png', isSecondImageVisible: false, title: 'a' },
+            { imgSrc1: 'client/resources/img/img49_s.png', imgSrc2: 'client/resources/img/img50_s.png', imgSrc: 'client/resources/img/img59_36s.png', isSecondImageVisible: false, title: 'a' },
+            { imgSrc1: 'client/resources/img/img49_s.png', imgSrc2: 'client/resources/img/img50_s.png', imgSrc: 'client/resources/img/img59_36s.png', isSecondImageVisible: false, title: 'a' },
+            { imgSrc1: 'client/resources/img/img49_s.png', imgSrc2: 'client/resources/img/img50_s.png', imgSrc: 'client/resources/img/img59_36s.png', isSecondImageVisible: false, title: 'a' }
          ]
       };
    },
@@ -51,4 +60,21 @@
    }
 }
 
-</script>
(No newline at end of file)
+</script>
+<style scoped>
+.popTxt {
+   width: 295px;
+   height: 406px;
+}
+
+.popTxt div:last-child img {
+   position: absolute;
+   top: 0;
+   left: 0;
+}
+
+.listenGroup .textbox {
+   top: 50%;
+    left: 50%;
+}
+</style>
(No newline at end of file)
client/views/pages/main/Chapter/Chapter2_4.vue
--- client/views/pages/main/Chapter/Chapter2_4.vue
+++ client/views/pages/main/Chapter/Chapter2_4.vue
@@ -19,11 +19,15 @@
             <div class="imgGroup">
 
                <div class="flex justify-center">
-                  <div class="btnGroup  flex" style="gap: 60px;">
+                  <div class="listenGroup  flex" style="gap: 60px;">
                      <button class="popTxt" v-for="(item, index) in items" :key="index" @click="updateContent(index)"
                         :class="{ active: selectedIndex === index }">
                         <img :src="item.imgSrc1">
                         <img :src="item.imgSrc2" v-if="selectedIndex === index" style="display: block;">
+                        <div class="textbox">
+                          <div style="height: 80%; line-height: 200px;"> <img :src="item.imgSrc" alt=""></div>
+                           <p class="subtitle3" style="height: 20%;">{{ item.title }}</p>
+                        </div>
                      </button>
                   </div>
                </div>
@@ -43,10 +47,10 @@
    data() {
       return {
          items: [
-            { imgSrc1: 'client/resources/img/img61_36s_1.png', imgSrc2: 'client/resources/img/img61_36s_1_Click.png' },
-            { imgSrc1: 'client/resources/img/img61_36s_2.png', imgSrc2: 'client/resources/img/img61_36s_2_Click.png' },
-            { imgSrc1: 'client/resources/img/img61_36s_3.png', imgSrc2: 'client/resources/img/img61_36s_4_Click.png' },
-            { imgSrc1: 'client/resources/img/img61_36s_4.png', imgSrc2: 'client/resources/img/img61_36s_3_Click.png' },
+            { imgSrc1: 'client/resources/img/img61_36s.png', imgSrc2: 'client/resources/img/img62_36s.png', imgSrc: 'client/resources/img/img59_36s.png', title: 'apple' },
+            { imgSrc1: 'client/resources/img/img61_36s.png', imgSrc2: 'client/resources/img/img62_36s.png', imgSrc: 'client/resources/img/img58_36s.png', title: 'a'},
+            { imgSrc1: 'client/resources/img/img61_36s.png', imgSrc2: 'client/resources/img/img62_36s.png', imgSrc: 'client/resources/img/img57_36s.png', title: 'a' },
+            { imgSrc1: 'client/resources/img/img61_36s.png', imgSrc2: 'client/resources/img/img62_36s.png', imgSrc: 'client/resources/img/img56_36s.png', title: 'a' },
          ],
          selectedIndex: 0,
          timer: "00",
@@ -68,10 +72,17 @@
 </script>
 
 <style scoped>
+.listenGroup{height: 305px;}
+.listenGroup button{position: relative;}
+.listenGroup .textbox {    
+   width: 184px;
+    height: 206px;
+    left: 46%;
+    top: 43%;} 
 .look-text{bottom: 50px;}
 .btnGroup{width: 1060px; height: 340px;}
-.popTxt{width: 216px;}
-.popTxt img {
+.popTxt{width: 216px; }
+.popTxt > img {
    position: absolute;
    top: 0;
    left: 0;
client/views/pages/main/Chapter/Chapter2_9.vue
--- client/views/pages/main/Chapter/Chapter2_9.vue
+++ client/views/pages/main/Chapter/Chapter2_9.vue
@@ -17,15 +17,67 @@
           <div class="text-ct">
           <div class="dropGroup flex align-center justify-center">
             <div style="position: relative;" >
-              <img src="../../../../resources/img/img72_41s_1.png" alt="" style="width: 90%;">
-              <button style="left: 141px;
-    top: 60px;"><img src="../../../../resources/img/img78_41s.png" alt=""></button>
-              <button style="    left: 200px;
-    bottom: 86px;"><img src="../../../../resources/img/img79_41s.png" alt=""></button>
-              <button style="    right: 237px;
-    top: 159px;"><img src="../../../../resources/img/img80_41s.png" alt=""></button>
-              <button style="     right: 159px;
-    bottom: 46px;"><img src="../../../../resources/img/img81_41s.png" alt=""></button>
+              <img src="../../../../resources/img/img72_41s.png" alt="" style="width: 93%;">
+              <div class="vocaGroup">
+                <div class="flex justify-between mb80">
+                    <article class="flex align-center">
+                        <div class="imgGroup mr30"><img src="../../../../resources/img/img74_41s.png" data-num="1"></div>
+                        <div class="flex align-start">
+                             <div>
+                                <h3>sidewalk</h3>
+                                   <div class="flex align-center mt10">
+                                      <span class="title1">보도</span>
+                                   </div>
+                           </div>
+                        </div>
+                    </article>
+                    <article class="flex align-center">
+                        <div class="imgGroup mr30"><img src="../../../../resources/img/img75_41s.png" data-num="1"></div>
+                        <div class="flex align-start">
+                             <div>
+                                <h3>sidewalk</h3>
+                                   <div class="flex align-center mt10">
+                                      <span class="title1">보도</span>
+                                   </div>
+                           </div>
+                        </div>
+                    </article>
+                </div>
+                <div class="flex justify-between">
+                    <article class="flex align-center">
+                        <div class="imgGroup mr30"><img src="../../../../resources/img/img76_41s.png" data-num="1"></div>
+                        <div class="flex align-start">
+                             <div>
+                                <h3>sidewalk</h3>
+                                   <div class="flex align-center mt10">
+                                      <span class="title1">보도</span>
+                                   </div>
+                           </div>
+                        </div>
+                    </article>
+                    <article class="flex align-center">
+                        <div class="imgGroup mr30"><img src="../../../../resources/img/img77_41s.png" data-num="1"></div>
+                        <div class="flex align-start">
+                             <div>
+                                <h3>sidewalk</h3>
+                                   <div class="flex align-center mt10">
+                                      <span class="title1">보도</span>
+                                   </div>
+                           </div>
+                        </div>
+                    </article>
+                </div>
+              </div>
+             <div class="textbox">
+                <button style="left: 141px;
+      top: 44px;"><img src="../../../../resources/img/img78_41s.png" alt=""></button>
+                <button style="    left: 200px;
+      bottom: 46px;"><img src="../../../../resources/img/img79_41s.png" alt=""></button>
+                <button style="    right: 237px;
+      top: 114px;"><img src="../../../../resources/img/img80_41s.png" alt=""></button>
+                <button style="     right: 159px;
+      bottom: 5px;"><img src="../../../../resources/img/img81_41s.png" alt=""></button>
+             </div>
                
             </div>
           </div>
@@ -68,4 +120,8 @@
   }
   </script>
   <style scoped>
+  .vocaGroup{    width: 79%;
+    position: absolute;
+    top: 90px;
+    left: 120px;}
 </style>
(No newline at end of file)
client/views/pages/main/Dashboard.vue
--- client/views/pages/main/Dashboard.vue
+++ client/views/pages/main/Dashboard.vue
@@ -118,17 +118,16 @@
                         </button>
                         <p class="long">최종 평가</p>
                     </div>
+
                 </div>
-                <div class="rabbit-end" @click="ShowPopup"><img src="../../../resources/img/img138_72s.png" alt=""></div>
             </div>
-                </div>
-                <!-- 팝업 -->
-                <div v-show="searchOpen2" class="popup-wrap">
-                    <div class="popup-box">
-                        <button type="button" class="popup-close-btn" style="position:absolute; top:10px; right: 10px;"
-                            @click="closeModal">
-                            <svg-icon type="mdi" :path="mdiWindowClose" class="close-btn"></svg-icon>
-                        </button>
+            <!-- 팝업 -->
+            <div v-show="searchOpen2" class="popup-wrap">
+                <div class="popup-box">
+                    <button type="button" class="popup-close-btn" style="position:absolute; top:10px; right: 10px;"
+                        @click="closeModal">
+                        <svg-icon type="mdi" :path="mdiWindowClose" class="close-btn"></svg-icon>
+                    </button>
 
                         <div class="mb30 text-ct">
                             <p class="title1 mb20">1단원이 끝났습니다!</p>
@@ -264,23 +263,22 @@
             </div>
         </div>
 
-        <div v-else>
-            <!-- 2번 템플릿 -->
-            <div class="myplan">
-                <div class="title-box flex justify-between mb40">
-                    <p class="title">지금은 학습 루트가 등록이 안됐어요 ! 학습 일정에서 학습루트를 등록해볼까요 ?</p>
+        </div>
+        <div class="complete-wrap mt90 myphoto">
+            <h2 class="mb40">이 단원을 끝낸 친구들</h2>
+            <article class=" flex-column" style="gap: 5px;">
+                <div class="flex" style="gap: 5px;">
+                    <div @click="buttonSearch2" class="photo"><img src="../../../resources/img/img143_75s.png" alt=""></div>
+                    <div @click="buttonSearch" class="photo"><img src="../../../resources/img/img143_75s.png" alt=""></div>
                 </div>
-                <div class="wrap" style="border-radius: 0; min-height: 197px;">
-                   <p class="title1"> 오늘 학습할 내용이 없습니다.</p>
-                </div>
-                <div class="yellow-box mt30">
-                    <div class="title-box flex justify-between align-center">
-                        <div >
-                            <p class="title">오늘 공부를 계획해봅시다.</p>
-                            <p class="title1 mt20"> 스스로 학습 일정을 바꿔볼까요?</p>
-                        </div>
-                        <button type="button" title="바로가기" class="yellow-btn" @click="goToPage('MyPlan2')">
-                            바로가기
+            </article>
+            <!-- 팝업 -->
+            <article class="popup-wrap" v-show="searchOpen">
+                <div class="popup-box ">
+                    <div class="flex mb10  justify-between">
+                        <p class="popup-title">알림</p>
+                        <button type="button" class="popup-close-btn" @click="closeBtn2">
+                            <svg-icon type="mdi" :path="mdiWindowClose" class="close-btn"></svg-icon>
                         </button>
                     </div>
                 </div>
@@ -395,6 +393,9 @@
                 this.searchOpen2 = true;
             }
         },
+        ShowPopup() {
+            this.searchOpen2 = true;  // 촬영 여부 묻는 모달창 열기
+        },
         updateContent(index) {
             this.selectedIndex = index;
 
@@ -429,12 +430,14 @@
                     console.log("error>>>>>>>>", error);
                 });
         },
-        closeModal() {  //웹캠 팝업 닫기
+        closeModal() {  //웹캠 및 모든 팝업 닫기
             // this.showModal = false;
+            this.searchOpen = false;
             this.searchOpen2 = false;
             this.showCameraModal = false;
             this.photoTaken = false;
             this.photo = null;
+            this.showPhotoModal = false;
 
             //스트림 종료
             if (this.stream) {
@@ -887,4 +890,8 @@
 .toolbar input {
     margin: 5px;
 }
+
+.rabbit-end {
+    cursor: pointer;
+}
 </style>
(No newline at end of file)
client/views/pages/main/MyPage.vue
--- client/views/pages/main/MyPage.vue
+++ client/views/pages/main/MyPage.vue
@@ -98,7 +98,7 @@
                     </div>
                     <div class="box " style="gap: 10px;">
                         <div><img src="../../../resources/img/img196_12p.png" alt=""></div>
-                        <P class="title2 mt10">현재 30명 중 <em class="blue">2등</em>입니다.</P>
+                        <P class="title2 mt10">현재 {{ stdCount }}명 중 <em class="blue">{{ rank }}등</em>입니다.</P>
                     </div>
                 </div>
                 <div class="textbook book-navy">
@@ -121,11 +121,15 @@
 import { mdiMagnify,  } from '@mdi/js';
 import { mdilArrowRight } from '@mdi/light-js';
 import ProgressBar from '../../component/ProgressBar.vue';
+import axios from "axios";
 
 
 export default {
     data() {
         return {
+            stdCount : 0,
+            rank: 0,
+
             mdiMagnify: mdiMagnify,
             mdilArrowRight: mdilArrowRight,
             timer: "00:00",
@@ -133,6 +137,51 @@
         }
     },
     methods: {
+        classStdCount: function () {
+            const vm = this;
+            axios({
+                url: "/userclass/classStdCount.json",
+                method: "post",
+                headers:{
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    sclsId:"1"
+                }
+            })
+            .then(function (response) {
+                console.log("classStdCount - response : ", response.data);
+                vm.stdCount = response.data;
+            })
+            .catch(function (error) {
+                console.log("classStdCount - error : ", error);
+                alert("반 학생 수 조회에 오류가 발생했습니다.");
+            });
+        },
+
+        photoRankByLikeData: function () {
+            const vm = this;
+            axios({
+                url: "/photo/photoRankByLikeData.json",
+                method: "post",
+                headers:{
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    sclsId:"1",
+                    stdId:"1"
+                }
+            })
+            .then(function (response) {
+                console.log("Rank - response : ", response.data);
+                vm.rank = response.data;
+            })
+            .catch(function (error) {
+                console.log("Rank - error : ", error);
+                alert("학생 사진 랭킹 조회에 오류가 발생했습니다.");
+            });
+        },
+
         goToPage(page) {
             this.$router.push({ name: page });
         },
@@ -168,6 +217,8 @@
     },
     mounted() {
         console.log('Main2 mounted');
+        this.classStdCount();
+        this.photoRankByLikeData();
     }
 }
 </script>
client/views/pages/main/MyPlan.vue
--- client/views/pages/main/MyPlan.vue
+++ client/views/pages/main/MyPlan.vue
@@ -1,31 +1,72 @@
 <template>
-    <div class="myplan">
-        <div class="title-box flex justify-between mb40">
-            <p class="title">오늘 공부할 내용을 확인해봅시다.</p>
-            <!-- <select name="" id="">
-                <option value="">A반</option>
-            </select> -->
-        </div>
-        <div class="wrap" style="border-radius: 0; min-height: 197px;">
-           <p class="title1"> 오늘 학습할 내용이 없습니다.</p>
-        </div>
-        <div class="yellow-box mt30">
-            <div class="title-box flex justify-between align-center">
-                <div >
-                    <p class="title">오늘 공부를 계획해봅시다.</p>
-                    <p class="title1 mt20"> 스스로 학습 일정을 바꿔볼까요?</p>
+
+    <div class="main">
+        <div class="myplan">
+            <div class="title-box flex justify-between mb40">
+                <p class="title">오늘 공부할 내용을 확인해봅시다.</p>
+            </div>
+            <div class="wrap" style="border-radius: 0; min-height: 197px;"> 
+                <p class="title1" v-if="!schedules || schedules.length === 0"> 오늘 학습할 내용이 없습니다.</p>
+ 
+                <div class="flex-column" style="gap: 20px;" v-else v-for="(schedule, index) in schedules" :key="index">
+                    <div class="flex justify-between align-center" style="gap: 70px;">
+                        <div><img src="../../../resources/img/img217_22s.png" alt=""></div>
+                        <div class="wrap cs-pt" :class="{ 'cs-pt-clicked': isClicked }" style="width: 100%;">
+                            <div class="text-lf flex justify-between align-center">
+                                <div>
+                                    <p class="title2">grade 3</p>
+                                    <div class="flex align-center mb10" style="gap: 10px;">
+                                        <p class="title2"><em class="gray-bd">{{ schedule.schdl_unit }}교시</em></p>
+                                        <p class="title1">{{ schedule.schedule_time }}</p> 
+                                    </div>
+                                    <div class="title-box mb10">
+                                        <span class="title">{{schedule.unit_nm}}</span> 
+                                    </div>
+                                    <p class="title2">{{ schedule.book_nm }}</p>
+                                    <!-- <p class="title2">wirte a</p> -->
+                                </div>
+                                <div><img src="../../../resources/img/img214_19s.png" alt=""></div>
+                            </div>
+                        </div>
+                    </div>
                 </div>
-                <button type="button" title="바로가기" class="yellow-btn" @click="goToPage('MyPlan2')">
-                    바로가기
-                </button>
+            </div>
+
+            <div class="yellow-box mt30">
+                <div class="title-box flex justify-between align-center">
+                    <div>
+                        <p class="title">오늘 공부를 계획해봅시다.</p>
+                        <p class="title1 mt20"> 스스로 학습 일정을 바꿔볼까요?</p>
+                    </div>
+                    <button type="button" title="바로가기" class="yellow-btn" @click="goToPage('MyPlan2')">
+                        바로가기
+                    </button>
+                </div>
+            </div>
+            <div class="wrap mt30">
+                <p class="title1 mb20"> AI 맞춤형 학습 코스는 어떨까요?</p>
+                <div class="imgGroup flex justify-between">
+                    <div class="text-lf">
+                        <img src="../../../resources/img/img215_22s.png" alt="">
+                        <p class="title2 mt10">추천 학습 단원</p>
+                    </div>
+                    <div class="text-lf">
+                        <img src="../../../resources/img/img215_22s.png" alt="">
+                        <p class="title2 mt10">추천 학습 단원</p>
+                    </div>
+                    <div class="text-lf">
+                        <img src="../../../resources/img/img215_22s.png" alt="">
+                        <p class="title2 mt10">추천 학습 단원</p>
+                    </div>
+                </div>
             </div>
         </div>
     </div>
 
-    
 </template>
 
 <script>
+import axios from "axios";
 import SvgIcon from '@jamescoyle/vue-icon';
 import { mdiMagnify, mdiWindowClose } from '@mdi/js';
 
@@ -37,6 +78,16 @@
             mdiWindowClose: mdiWindowClose,
             showModal: false,
             searchOpen: false,
+            schedules: [],
+            stdId: "2",
+            timeList: [
+                { label: "1교시", time: "08:00 ~ 09:00", value: "1" },
+                { label: "2교시", time: "09:00 ~ 10:00", value: "2" },
+                { label: "3교시", time: "10:00 ~ 11:00", value: "3" },
+                { label: "4교시", time: "11:00 ~ 12:00", value: "4" },
+                { label: "5교시", time: "13:00 ~ 14:00", value: "5" },
+                { label: "6교시", time: "14:00 ~ 15:00", value: "6" },
+            ],
         }
     },
     methods: {
@@ -66,8 +117,27 @@
         closeBtn() {
             this.searchOpen = false;
 
-        },
-
+        }, fetchSchedules() {
+            axios.post("/schedule/selectSchedule.json", { stdId: this.stdId }, {
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                }
+            })
+                .then(response => {
+                    this.schedules = response.data.map(schedule => { 
+                        const matchingTime = this.timeList.find(time => time.value === schedule.schdl_unit);
+                        return {
+                            ...schedule,
+                            schedule_time: matchingTime ? matchingTime.time : "시간 정보 없음"
+                        };
+                    });
+                    console.log(this.schedules);
+                })
+                .catch(error => {
+                    console.error("fetchUnits - error:", error);
+                    alert("단원 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        }
     },
     watch: {
 
@@ -80,6 +150,7 @@
     },
     mounted() {
         console.log('Main2 mounted');
+        this.fetchSchedules();
     }
 }
 </script>
(No newline at end of file)
client/views/pages/main/MyPlan2.vue
--- client/views/pages/main/MyPlan2.vue
+++ client/views/pages/main/MyPlan2.vue
@@ -2,19 +2,13 @@
     <div class=" main">
         <div class="myplan">
             <div class="title-box flex justify-between mb40">
-            <p class="title">오늘 공부를 계획해봅시다.</p>
-            <!-- <select name="" id="">
-                <option value="">A반</option>
-            </select> -->
-        </div>
+                <p class="title">오늘 공부를 계획해봅시다.</p>
+            </div>
             <div class="wrap">
                 <p class="title1"> 학습 교재를 선택합시다.</p>
                 <div class="search-wrap flex justify-end mb20 mt20">
-                    <!-- <select name="" id="" class="mr10 data-wrap">
-                       <option value="">전체</option>
-                   </select> -->
-                    <input class="data-wrap" type="text" placeholder="검색하세요.">
-                    <button type="button" title="위원회 검색">
+                    <input class="data-wrap" type="text" placeholder="검색하세요." v-model="keyword">
+                    <button type="button" title="위원회 검색" @click="searchBook">
                         <img src="../../../resources/img/look_t.png" alt="">
                     </button>
                 </div>
@@ -26,93 +20,53 @@
                             <col style="width: 70%;">
                             <col style="width: 15%;">
                         </colgroup>
-                        <tr>
-                            <td><input type="checkbox" class="ui-checkbox mr10"></td>
+                        <tr v-for="(book, index) in books" :key="index">
+                            <td><input type="checkbox" class="ui-checkbox mr10" v-model="book.isSelected"></td>
                             <td><img src="../../../resources/img/img214_19s.png" alt=""></td>
                             <td class="text-lf">
-                                <p class="title1">the best</p>
-                                <p class="title2">wirte a</p>
+                                <p class="title1">{{ book.book_nm }}</p>
+                                <p class="title2">grade 3</p>
                             </td>
-                            <td> <button type="button" title="확인" class="new-btn" @click="buttonSearch">
-                                    시간입력
-                                </button></td>
-                        </tr>
-                        <tr>
-                            <td><input type="checkbox" class="ui-checkbox mr10"></td>
-                            <td><img src="../../../resources/img/img214_19s.png" alt=""></td>
-                            <td class="text-lf">
-                                <p class="title1">the best</p>
-                                <p class="title2">wirte a</p>
-                            </td>
-                            <td> <button type="button" title="확인" class="new-btn" @click="buttonSearch">
+                            <td> <button type="button" title="확인" class="new-btn"
+                                    @click="buttonSearch(book.book_nm, book.book_id)">
                                     시간입력
                                 </button></td>
                         </tr>
                     </table>
                     <div class="flex justify-end">
-                        <button type="button" title="선택하기" class="yellow-btn mt30">
+                        <button type="button" title="선택하기" class="yellow-btn mt30" @click="checkSchedule">
                             선택하기
                         </button>
                     </div>
                 </div>
             </div>
-            <div class="wrap mt30">
-                <p class="title1 mb20"> AI 맞춤형 학습 코스는 어떨까요?</p>
-                <div class="imgGroup flex justify-between">
-                    <div class="text-lf">
-                        <img src="../../../resources/img/img215_22s.png" alt="">
-                        <p class="title2 mt10">추천 학습 단원</p>
-                    </div>
-                    <div class="text-lf">
-                        <img src="../../../resources/img/img215_22s.png" alt="">
-                        <p class="title2 mt10">추천 학습 단원</p>
-                    </div>
-                    <div class="text-lf">
-                        <img src="../../../resources/img/img215_22s.png" alt="">
-                        <p class="title2 mt10">추천 학습 단원</p>
-                    </div>
-                </div>
-            </div>
+
             <div class="wrap mt30">
                 <p class="title1 mb20">학습일정을 확인해봅시다. </p>
-                <div class="flex-column" style="gap: 20px;">
+                <div class="flex-column" style="gap: 20px;" v-for="(book, index) in DataArray" :key="index">
                     <div class=" flex justify-between align-center " style="gap: 70px;">
                         <div><img src="../../../resources/img/img217_22s.png" alt=""></div>
-                        <div class="wrap cs-pt" :class="{ 'cs-pt-clicked': isClicked }" @click="toggleClicked" style="width: 100%;">
+                        <div class="wrap cs-pt" :class="{ 'cs-pt-clicked': isClicked }" style="width: 100%;">
                             <div class="text-lf flex justify-between align-center ">
-                               <div>
+                                <div>
                                     <div class="flex align-center mb10" style="gap: 10px;">
-                                        <p class="title2"><em class="gray-bd">1교시</em></p>
-                                        <p class="title1">9:00</p>
-                                        <p class="title1">~</p>
-                                        <p class="title1">10:00</p>
+                                        <p class="title2"><em class="gray-bd">{{ book.scheduleUnit }}교시</em></p>
+                                        <p class="title1">{{ book.scheduleTime }}</p>
                                     </div>
-                                    <div class="title-box  mb10"> <span class="title">the best</span></div>
+                                    <div class="title-box  mb10"> <span class="title">{{ book.bookNm }}</span></div>
                                     <p class="title2">wirte a</p>
-                               </div>
-                               <div class=""> <img src="../../../resources/img/img214_19s.png" alt=""></div>
+                                </div>
+                                <div class=""> <img src="../../../resources/img/img214_19s.png" alt=""></div>
                             </div>
                         </div>
                     </div>
-                    
                 </div>
                 <div class="flex justify-end">
-                        <button type="button" title="선택하기" class="yellow-btn mt30">
-                            선택하기
-                        </button>
-                    </div>
-            </div>
-            <!-- <div class="yellow-box mt30">
-                <div class="title-box flex justify-between align-center">
-                    <div>
-                        <p class="title">학습 일정 변경</p>
-                        <p class="title1 mt20"> 스스로 학습 일정을 바꿔볼까요?</p>
-                    </div>
-                    <button type="button" title="바로가기" class="yellow-btn" @click="goToPage('MyPlan2')">
-                    바로가기
-                </button>
+                    <button type="button" title="저장하기" class="yellow-btn mt30" @click="insertSchedule">
+                        저장하기
+                    </button>
                 </div>
-            </div> -->
+            </div>
             <div class="popup-wrap" v-show="searchOpen">
                 <div class="popup-box ">
                     <div class="flex mb10  justify-between">
@@ -121,74 +75,25 @@
                             <svg-icon type="mdi" :path="mdiWindowClose" class="close-btn"></svg-icon>
                         </button>
                     </div>
-                    <article class=" mb20 flex-column " style="gap: 20px;">
-                        <div class="flex justify-between bg-gray">
-                            <div class="flex align-center">
-                                <div><input type="checkbox" class="ui-checkbox mr20"></div>
-                                <div class="text-lf">
-                                    <p class="title1">2교시</p>
+                    <article class="mb20 flex-column" style="gap: 20px;">
+                        <div v-for="(item, index) in timeList" :key="item.value" class="flex justify-between bg-gray">
+                            <div class="flex items-center">
+                                <div>
+                                    <input type="checkbox" class="ui-checkbox mr20" v-model="checkedItems"
+                                        :value="item" />
+                                </div>
+                                <div class="text-lg">
+                                    <p class="title1">{{ item.label }}</p>
                                 </div>
                             </div>
-                            <div>
-                                <div class="flex align-center " style="gap: 10px;"><img
-                                        src="../../../resources/img/img215_20s.png" alt="">
-                                    <p class="title1">9:00</p>
-                                    <p class="title1">~</p>
-                                    <p class="title1">10:00</p>
-                                </div>
-                            </div>
-                        </div>
-                        <div class="flex justify-between bg-gray">
-                            <div class="flex align-center">
-                                <div><input type="checkbox" class="ui-checkbox mr20"></div>
-                                <div class="text-lf">
-                                    <p class="title1">2교시</p>
-                                </div>
-                            </div>
-                            <div>
-                                <div class="flex align-center " style="gap: 10px;"><img
-                                        src="../../../resources/img/img215_20s.png" alt="">
-                                    <p class="title1">9:00</p>
-                                    <p class="title1">~</p>
-                                    <p class="title1">10:00</p>
-                                </div>
-                            </div>
-                        </div>
-                        <div class="flex justify-between bg-gray">
-                            <div class="flex align-center">
-                                <div><input type="checkbox" class="ui-checkbox mr20"></div>
-                                <div class="text-lf">
-                                    <p class="title1">2교시</p>
-                                </div>
-                            </div>
-                            <div>
-                                <div class="flex align-center " style="gap: 10px;"><img
-                                        src="../../../resources/img/img215_20s.png" alt="">
-                                    <p class="title1">9:00</p>
-                                    <p class="title1">~</p>
-                                    <p class="title1">10:00</p>
-                                </div>
-                            </div>
-                        </div>
-                        <div class="flex justify-between bg-gray">
-                            <div class="flex align-center">
-                                <div><input type="checkbox" class="ui-checkbox mr20"></div>
-                                <div class="text-lf">
-                                    <p class="title1">2교시</p>
-                                </div>
-                            </div>
-                            <div>
-                                <div class="flex align-center " style="gap: 10px;"><img
-                                        src="../../../resources/img/img215_20s.png" alt="">
-                                    <p class="title1">9:00</p>
-                                    <p class="title1">~</p>
-                                    <p class="title1">10:00</p>
-                                </div>
+                            <div class="flex align-center" style="gap: 10px;">
+                                <img src="../../../resources/img/img215_20s.png" alt="" />
+                                <p class="title1">{{ item.time }}</p>
                             </div>
                         </div>
                     </article>
                     <div class="flex justify-center mt20">
-                        <button type="button" title="선택" class="new-btn">
+                        <button type="button" title="선택" class="new-btn" @click="timePick">
                             선택
                         </button>
                     </div>
@@ -200,9 +105,9 @@
 </template>
 
 <script>
+import axios from "axios";
 import SvgIcon from '@jamescoyle/vue-icon';
 import { mdiMagnify, mdiWindowClose } from '@mdi/js';
-
 
 export default {
     data() {
@@ -212,12 +117,28 @@
             mdiWindowClose: mdiWindowClose,
             showModal: false,
             searchOpen: false,
+            books: [],
+            timeList: [
+                { label: "1교시", time: "08:00 ~ 09:00", value: "1" },
+                { label: "2교시", time: "09:00 ~ 10:00", value: "2" },
+                { label: "3교시", time: "10:00 ~ 11:00", value: "3" },
+                { label: "4교시", time: "11:00 ~ 12:00", value: "4" },
+                { label: "5교시", time: "13:00 ~ 14:00", value: "5" },
+                { label: "6교시", time: "14:00 ~ 15:00", value: "6" },
+            ],
+            checkedItems: [],
+            newDataArray: [],
+            DataArray: [],
+            nowName: "",
+            nowID: "",
+            keyword: "",
+            unitList: []
         }
     },
     methods: {
         toggleClicked() {
-      this.isClicked = !this.isClicked;
-    },
+            this.isClicked = !this.isClicked;
+        },
         goToPage(page) {
             this.$router.push({ name: page });
         },
@@ -238,32 +159,130 @@
         closeModal() {
             this.showModal = false;
         },
-        buttonSearch() {
+        buttonSearch(book_nm, book_id) {
+            this.nowID = book_id
+            this.nowName = book_nm
             this.searchOpen = true;
         },
         closeBtn() {
             this.searchOpen = false;
-
         },
+        timePick() {
+            const selectedBooks = this.books.filter(book => book.isSelected);
+            if (selectedBooks.length <= 0) {
+                alert('교재를 선택해 주세요.');
+            } else {
+                if (this.checkedItems.length === 1) {
+                    if (this.unitList.find(item => item === this.checkedItems[0])) {
+                        alert('이미 지정된 시간입니다.');
+                    } else {
+                        this.addToNewDataArray(this.checkedItems[0]);
+                        this.searchOpen = false;
+                        this.nowID = ""
+                        this.nowName = ""
+                        this.unitList.push(this.checkedItems[0])
+                        this.checkedItems = []
+                    }
+                } else if (this.checkedItems.length > 1) {
+                    alert('시간은 하나만 선택할 수 있습니다.');
+                } else if (this.checkedItems.length <= 0) {
+                    alert('시간을 선택해 주세요.');
+                }
+            }
+        },
+        addToNewDataArray(selectedTime) {
+            if (!this.newDataArray) {
+                this.newDataArray = [];
+            }
+            const newEntry = {
+                bookNm: this.nowName,
+                scheduleTime: selectedTime.time,
+                scheduleUnit: selectedTime.value,
+                bookId: this.nowID,
+                stdId: "2"
+            };
+            this.newDataArray.push(newEntry);
+        },
+        async insertSchedule() {
 
+            this.newDataArray = this.DataArray.map(item => ({
+                scheduleUnit: item.scheduleUnit,
+                bookId: item.bookId,
+                stdId: item.stdId
+            }));
+            try {
+                for (let data of this.newDataArray) {
+                    await axios.post('/schedule/insertSchedule.json', data);
+                }
+                alert('학습 일정이 등록됐어요!');
+                this.goToPage('MyPlan')
+            } catch (error) {
+                console.error('데이터 제출 중 오류 발생:', error);
+                alert('데이터 제출 중 오류가 발생했습니다.');
+            }
+        }, checkSchedule() {
+            const sortedArray = this.newDataArray.slice().sort((a, b) => {
+                return a.scheduleUnit - b.scheduleUnit;
+            });
+
+            this.DataArray = sortedArray;
+        },
+        fetchBooks() {
+            axios({
+                url: "/book/findAll.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+            })
+                .then(response => {
+                    this.books = response.data.map(book => ({
+                        ...book,
+                        isSelected: false
+                    }));
+                })
+                .catch(error => {
+                    console.error("fetchBooks - error: ", error);
+                    alert("교재 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
+        searchBook() {
+            this.books = ""
+            axios.post("/book/search.json", { book_nm: this.keyword }, {
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                }
+            })
+                .then(response => {
+                    this.books = response.data.list.map(book => ({
+                        ...book,
+                        isSelected: false
+                    }));
+                })
+                .catch(error => {
+                    console.error("fetchUnits - error:", error);
+                    alert("단원 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        }
     },
     watch: {
-
+        // Watchers for reactivity (if needed)
     },
     computed: {
-
+        // Computed properties (if needed)
     },
     components: {
         SvgIcon
     },
     mounted() {
         console.log('Main2 mounted');
+        this.fetchBooks();
     }
 }
 </script>
+
 <style scoped>
 .popup-box {
     width: 750px;
-
 }
 </style>
(No newline at end of file)
client/views/pages/teacher/VocaDetail.vue
--- client/views/pages/teacher/VocaDetail.vue
+++ client/views/pages/teacher/VocaDetail.vue
@@ -1,6 +1,6 @@
 <template>
     <div class="title-box flex justify-between mb40">
-        <p class="title">단어장 등록</p>
+        <p class="title">단어장 수정</p>
     </div>
     <div class="board-wrap">
         <div class="flex align-center mb20">
@@ -36,7 +36,7 @@
         </div>
     </div>
     <div class="flex justify-between mt50">
-        <button type="button" title="글쓰기" class="new-btn" @click="goToPage('TextList')">
+        <button type="button" title="글쓰기" class="new-btn" @click="goToPage('VocaList')">
             목록
         </button>
         <div class="flex">
client/views/pages/teacher/VocaInsert.vue
--- client/views/pages/teacher/VocaInsert.vue
+++ client/views/pages/teacher/VocaInsert.vue
@@ -2,48 +2,56 @@
     <div class="title-box flex justify-between mb40">
         <p class="title">단어장 등록</p>
     </div>
+    <div class="title2 gray flex mb40">{{ titleMessage }}</div>
     <div class="board-wrap">
         <div class="flex align-center mb20">
             <label for="" class="title2">지문</label>
-            <select v-model="selectedSearchOption" class="mr10 data-wrap">
-                <option value="bbsTtl">제목</option>
-                <option value="bbsCnt">내용</option>
-                <option value="userNm">작성자</option>
-                <option value="bbsCls">카테고리</option>
+            <select v-model="selectedTextId" class="mr10 data-wrap">
+                <option v-for="text in texts" :key="text.textId" :value="text.textId">
+                    {{ text.textTtl }}
+                </option>
+            </select>
+        </div>
+        <div class="flex align-center mb20">
+            <label for="" class="title2">단어장 타입</label>
+            <select v-model="selectedWdBookTypeId" class="mr10 data-wrap">
+                <option value="1">단어장 (일반)</option>
+                <option value="2">단어장 (스피킹)</option>
+                <option value="3">단어장 (숏폼)</option>
+                <option value="4">단어장 (카드 뒤집기)</option>
+                <option value="5">단어장 (카드 맞추기)</option>
             </select>
         </div>
         <div class="flex align-center">
             <label for="" class="title2">단어 목록</label>
             <div class="flex-column" style="gap: 10px;">
                 <div class="flex align-center" style="gap: 10px;">
-                    <input type="text" class="data-wrap" placeholder="영어">
-                    <input type="text" class="data-wrap" placeholder="한글">
-                    <button type="button" @click="addThesis">
+                    <input v-model="newWord.eng" type="text" class="data-wrap" placeholder="영어">
+                    <input v-model="newWord.kor" type="text" class="data-wrap" placeholder="한글">
+                    <button type="button" @click="addWord">
                         <img src="../../../resources/img/btn39_120t_normal.png" alt="">
-
                     </button>
                 </div>
-                <div class="flex align-center " style="gap: 10px;" v-for="(thesis, index) in thesised" :key="thesis.id">
-                    <input type="text" class="data-wrap" placeholder="영어">
-                    <input type="text" class="data-wrap" placeholder="한글">
-                    <button type="button" @click="removeThesis(thesis.id)">
-                        <img src="../../../resources/img/btn38_120t_normal.png" alt="">
-                    </button>
 
+                <!-- 여기에 단어장에 소속될 단어들 태그 형태 리스트 -->
+                <div v-for="(word, index) in words" :key="index" class="word-item flex align-center" style="gap: 10px;">
+                    <span>{{ word.eng }} / {{ word.kor }}</span>
+                    <button type="button" @click="removeWord(index)">삭제</button>
                 </div>
+
             </div>
 
         </div>
     </div>
     <div class="flex justify-between mt50">
-        <button type="button" title="글쓰기" class="new-btn" @click="goToPage('TextList')">
+        <button type="button" title="목록" class="new-btn" @click="goToPage('VocaList')">
             목록
         </button>
         <div class="flex">
-            <button type="button" title="글쓰기" class="new-btn mr10">
+            <button type="button" title="취소" class="new-btn mr10" @click="cancelAction">
                 취소
             </button>
-            <button type="button" title="글쓰기" class="new-btn">
+            <button type="button" title="등록" class="new-btn" @click="registerWordBook">
                 등록
             </button>
         </div>
@@ -52,41 +60,199 @@
 
 <script>
 import SvgIcon from '@jamescoyle/vue-icon';
-import { mdiMagnify, mdiPlusCircleOutline, mdiWindowClose } from '@mdi/js';
-
+import axios from "axios";
 
 export default {
     data() {
         return {
-            thesised: [],
-            mdiPlusCircleOutline: mdiPlusCircleOutline,
-            mdiMagnify: mdiMagnify,
-            mdiWindowClose: mdiWindowClose,
+            selectedBookId: null, // 추가될 단어장의 소속 책
+            selectedUnitId: null, // 추가될 단어장의 소속 단원
+            bookName: '', // 책 이름
+            unitName: '', // 단원 이름
+            titleMessage: '', // 등록 경로 메시지
+            texts: [], // 지문 목록
+            selectedTextId: null, // 선택된 지문 ID
+            selectedWdBookTypeId: '1', // 선택된 단어장 타입 ID
+            newWord: { eng: '', kor: '' }, // 입력된 새 단어
+            words: [], // 단어 목록
+            existingWords: [], // 기존 단어 목록 저장
+            userId: "2"
         }
     },
     methods: {
-        // 논문실적 버튼 추가
-        addThesis() {
-            // 고유 ID로 현재 시간의 타임스탬프를 사용
-            const uniqueId = Date.now();
-            this.thesised.push({
-                id: uniqueId, // 고유 ID 추가
 
+        // 책과 단원 이름을 가져오는 메서드
+        fetchBookAndUnitNames() {
+            // 책 이름 가져오기
+            axios.post('/book/findAll.json')
+                .then(response => {
+                    const book = response.data.find(book => book.book_id === this.selectedBookId);
+                    if (book) {
+                        this.bookName = book.book_nm;
+                        this.updateTitleMessage(); // 책 이름을 가져온 후에 제목 업데이트
+                    }
+                })
+                .catch(error => {
+                    console.error("책 이름 가져오기 실패: ", error);
+                });
+
+            // 단원 이름 가져오기
+            axios.post('/unit/unitList.json', { bookId: this.selectedBookId })
+                .then(response => {
+                    const unit = response.data.find(unit => unit.unitId === this.selectedUnitId);
+                    if (unit) {
+                        this.unitName = unit.unitName;
+                        this.updateTitleMessage(); // 단원 이름을 가져온 후에 제목 업데이트
+                    }
+                })
+                .catch(error => {
+                    console.error("단원 이름 가져오기 실패: ", error);
+                });
+        },
+
+        // 등록 경로 메시지를 업데이트하는 메서드
+        updateTitleMessage() {
+            this.titleMessage = `[${this.bookName}]책 > [${this.unitName}]단원`;
+        },
+
+        // 지문 목록을 가져오는 메서드
+        fetchTexts() {
+            axios.post('/text/selectTextList.json', {
+                page: 1,
+                pageSize: 100
+            })
+            .then(response => {
+                this.texts = response.data.texts;
+            })
+            .catch(error => {
+                console.error("지문 목록 가져오기 실패: ", error);
             });
         },
-        removeThesis(thesisId) {
-            // ID를 기준으로 교육 정보 객체를 찾아서 삭제
-            const index = this.thesised.findIndex(thesis => thesis.id === thesisId);
-            if (index !== -1) {
-                this.thesised.splice(index, 1);
+
+        addWord() { // 단어 추가
+            if (this.newWord.eng && this.newWord.kor) {
+                this.words.push({ ...this.newWord });
+                this.newWord.eng = '';
+                this.newWord.kor = '';
+            } else {
+                console.log("단어 입력이 비어 있음");
             }
         },
-        // 
+
+        removeWord(index) { // 단어 삭제
+            this.words.splice(index, 1);
+        },
+
         goToPage(page) {
             this.$router.push({ name: page });
         },
+
+        cancelAction() {
+            this.$router.go(-1);
+        },
+
+        // 기존 단어 조회 메서드
+        fetchExistingWords(wdBookId) {
+            return axios.post('/word/getWordsByBookId.json', { wdBookId: wdBookId })
+                .then(response => {
+                    return response.data.words || [];
+                })
+                .catch(error => {
+                    console.error('기존 단어 목록 가져오기 실패:', error);
+                    return [];
+                });
+        },
+
+        async registerWordBook() {
+            const vm = this;
+
+            try {
+                const response = await axios.post('/wordbook/insert.json', {
+                    wdBookTypeId: vm.selectedWdBookTypeId,
+                    textId: vm.selectedTextId,
+                    userId: vm.userId,
+                    bookId: vm.selectedBookId,
+                    unitId: vm.selectedUnitId
+                });
+
+                const wdBookId = response.data.wdBookId;
+
+                // 기존 단어 목록 조회
+                const existingWords = await vm.fetchExistingWords(wdBookId);
+                vm.existingWords = existingWords;
+
+                const existingWordNames = existingWords.map(word => word.wdNm);
+                const wordsToInsert = [];
+                const wordsToUpdate = [];
+                const wordsToDelete = [];
+
+                // 새로 추가된 단어와 기존 단어 비교
+                vm.words.forEach(word => {
+                    if (existingWordNames.includes(word.eng)) {
+                        wordsToUpdate.push(word);
+                    } else {
+                        wordsToInsert.push(word);
+                    }
+                });
+
+                // 삭제된 단어 목록 찾기
+                existingWords.forEach(word => {
+                    if (!vm.words.find(newWord => newWord.eng === word.wdNm)) {
+                        wordsToDelete.push(word);
+                    }
+                });
+
+                // 단어 삽입
+                for (const word of wordsToInsert) {
+                    await axios.post('/word/insert.json', {
+                        wdBookId: wdBookId,
+                        wdNm: word.eng,
+                        wdMnng: word.kor,
+                        fileMngId: '1'
+                    });
+                }
+
+                // 단어 업데이트
+                for (const word of wordsToUpdate) {
+                    await axios.post('/word/update.json', {
+                        wdBookId: wdBookId,
+                        wdNm: word.eng,
+                        wdMnng: word.kor,
+                        fileMngId: '1'
+                    });
+                }
+
+                // 단어 삭제
+                for (const word of wordsToDelete) {
+                    const wordToDelete = existingWords.find(existingWord => existingWord.wdNm === word.wdNm);
+                    if (wordToDelete) {
+                        await axios.post('/word/delete.json', {
+                            wdBookId: wdBookId,
+                            wdId: wordToDelete.wdId
+                        });
+                    }
+                }
+
+                alert('단어장이 성공적으로 등록되었습니다.');
+                vm.goToPage('VocaList');
+                
+            } catch (error) {
+                console.error('단어장 등록 중 오류 발생:', error);
+                alert('단어장 등록에 실패했습니다.');
+            }
+        }
+
     },
     watch: {
+
+        // 데이터 변경 시 등록 경로 메시지 업데이트
+        selectedBookId() {
+            this.fetchBookAndUnitNames();
+        },
+
+        selectedUnitId() {
+            this.fetchBookAndUnitNames();
+        }
 
     },
     computed: {
@@ -96,7 +262,12 @@
         SvgIcon
     },
     mounted() {
-        console.log('Main2 mounted');
+        console.log('VocaInsert mounted');
+        // 쿼리 전달 받기
+        this.selectedBookId = this.$route.query.selectedBookId || null;
+        this.selectedUnitId = this.$route.query.selectedUnitId || null;
+        this.fetchTexts();
+        this.fetchBookAndUnitNames();
     }
 }
 </script>
(No newline at end of file)
client/views/pages/teacher/VocaList.vue
--- client/views/pages/teacher/VocaList.vue
+++ client/views/pages/teacher/VocaList.vue
@@ -14,14 +14,14 @@
         </button>
     </div>
     <div class="search-wrap flex justify-between mb20 align-center">
-        <div class="title2 gray flex"><div class="black">[{{ selectedUnitName }}]</div>단원 전체 목록</div>
+        <div class="title2 gray flex">{{ titleMessage }}</div>
         <div>
-            <select name="" id="" class="mr10 data-wrap">
-                <option value="">지문</option>
-                <option value="">단어</option>
+            <select v-model="searchType" class="mr10 data-wrap">
+                <option value="text">지문</option>
+                <option value="word">단어</option>
             </select>
-            <input  type="text" placeholder="검색하세요.">
-            <button type="button" title="단어장 검색">
+            <input v-model="searchQuery" type="text" placeholder="검색하세요.">
+            <button type="button" title="단어장 검색" @click="searchWordBooks">
                 <img src="../../../resources/img/look_t.png" alt="">
             </button>
         </div>
@@ -77,10 +77,13 @@
             selectedBookId: null, // 선택된 책 ID 저장 변수
             selectedUnitId: null, // 선택된 단원 ID 저장 변수
             selectedUnitName: '', // 선택된 단원의 이름 저장 변수
+            titleMessage: '', // 타이틀 메시지 변수
             dataList: [],
             currentPage: 0,
             itemsPerPage: 2,
-            totalPosts: 0
+            totalPosts: 0,
+            searchType: 'text', // 검색 종류를 저장할 변수
+            searchQuery: '' // 검색어를 저장할 변수
         }
     },
     methods: {
@@ -119,6 +122,8 @@
             this.selectedUnitId = unitId;
             const selectedUnit = this.units.find(unit => unit.unitId === unitId);
             this.selectedUnitName = selectedUnit ? selectedUnit.unitName : '';
+            this.searchQuery = ''; 
+            this.titleMessage = `[${this.selectedUnitName}] 단원 전체 목록`;
             this.dataSelectList(); // 단어장 목록 조회
         },
 
@@ -205,16 +210,93 @@
 
         // 페이지 이동 메서드
         goToViewPage(page) {
-            this.$router.push({ name: page });
+            this.$router.push({
+                name: page,
+                query: {
+                    selectedBookId: this.selectedBookId,
+                    selectedUnitId: this.selectedUnitId
+                }
+             });
+        },
+
+        // 검색 메서드 추가
+        searchWordBooks() {
+            const vm = this;
+            let url = '';
+            let data = {};
+
+            if (this.searchType === 'text') {
+                // 지문으로 검색
+                url = '/wordbook/findByTextTitle.json';
+                data = {
+                    unitId: vm.selectedUnitId,
+                    textTitle: vm.searchQuery,
+                    page: vm.currentPage + 1,
+                    pageSize: vm.itemsPerPage
+                };
+                this.titleMessage = `[${vm.searchQuery}]의 지문 검색 결과`;
+            } else if (this.searchType === 'word') {
+                // 단어로 검색
+                url = '/wordbook/findByWord.json';
+                data = {
+                    unitId: vm.selectedUnitId,
+                    word: vm.searchQuery,
+                    page: vm.currentPage + 1,
+                    pageSize: vm.itemsPerPage
+                };
+                this.titleMessage = `[${vm.searchQuery}]의 단어 검색 결과`;
+            }
+
+            axios.post(url, data)
+            .then(function (response) {
+                console.log("searchWordBooks - response: ", response.data);
+                const wordBooks = response.data.wordBooks;
+                vm.totalPosts = response.data.totalWordBooks;
+                
+                // 지문 제목 및 단어 목록 가져오기
+                const fetchDataPromises = wordBooks.map(wordBook => {
+                    const textTitlePromise = axios.post("/text/selectOneText.json", {
+                        textId: wordBook.textId
+                    }).then(textResponse => {
+                        wordBook.textTtl = textResponse.data[0].text_ttl;
+                    }).catch(error => {
+                        console.error(`${wordBook.textId}으로 지문 제목 가져오기 실패: `, error);
+                        wordBook.textTtl = '제목값없음'; // 오류 시 기본값 설정
+                    });
+
+                    const wordsPromise = axios.post("/word/getWordsByBookId.json", {
+                        wdBookId: wordBook.wdBookId
+                    }).then(wordsResponse => {
+                        const words = wordsResponse.data.map(word => word.wdNm);
+                        wordBook.wordsPreview = vm.generateWordsPreview(words);
+                    }).catch(error => {
+                        console.error(`${wordBook.wdBookId}으로 단어 목록 가져오기 실패: `, error);
+                        wordBook.wordsPreview = '단어값없음'; // 오류 시 기본값 설정
+                    });
+
+                    return Promise.all([textTitlePromise, wordsPromise]);
+                });
+
+                // 모든 데이터 가져오기 작업이 완료되면 dataList에 데이터 설정
+                Promise.all(fetchDataPromises).then(() => {
+                    vm.dataList = wordBooks;
+                });
+            })
+            .catch(function (error) {
+                console.log("searchWordBooks - error: ", error);
+                alert("단어장 검색에 오류가 발생했습니다.");
+            });
         },
     },
     watch: {
 
     },
     computed: {
+
         totalPages() {
             return Math.ceil(this.totalPosts / this.itemsPerPage);
         },
+
         paginationButtons() {
             let start = Math.max(0, this.currentPage - 2);
             let end = Math.min(start + 5, this.totalPages);
@@ -225,9 +307,11 @@
 
             return Array.from({ length: end - start }, (_, i) => start + i + 1);
         },
+
         startIndex() {
             return this.currentPage * this.itemsPerPage;
         }
+
     },
     components:{
         SvgIcon
Add a comment
List