jichoi / lms_front star
guntaek 09-13
240913 김건택 AIDashboard 및 문제 수정(수정중)
@fd960b090c86c1cf02d4afe3c6c141d3969f5e1b
client/views/pages/main/AIDashboard.vue
--- client/views/pages/main/AIDashboard.vue
+++ client/views/pages/main/AIDashboard.vue
@@ -1,10 +1,51 @@
 <template>
-  <p class="title1" v-show="isHidden">오늘 공부를 다했어요! 너무 고생했어요!</p>
-  <div class="main" v-show="!isHidden">
+  <p
+    class="title1"
+    v-if="state === 'finish'"
+    style="position: absolute; top: 50%; transform: translate(0, -50%)"
+  >
+    오늘 공부를 다했어요! 너무 고생했어요!
+  </p>
+  <div
+    class="title1"
+    v-else-if="state === 'notRegistered'"
+    style="position: absolute; top: 50%; transform: translate(0, -50%)"
+  >
+    <div>
+      <h3>
+        지금은 학습 루트가 등록이 안됐어요! 학습 일정에서 학습루트를
+        등록해볼까요?
+      </h3>
+      <button
+        type="button"
+        title="바로가기"
+        class="yellow-btn"
+        @click="goToPage2('MyPlan2')"
+      >
+        바로가기
+      </button>
+    </div>
+  </div>
+  <p
+    class="title1"
+    v-else-if="state === 'noProblem'"
+    style="position: absolute; top: 50%; transform: translate(0, -50%)"
+  >
+    교재에 등록된 문제가 없습니다.
+  </p>
+  <div v-else class="main">
     <div class="race-wrap-ai">
+      <!-- <div class="title-box">
+        <p class="title" style="margin-top: 7rem">{{ titleUnitName }}</p>
+        <p class="subtitle">{{ titleBookName }}</p>
+      </div> -->
       <div class="race-box">
         <div class="rabbit-start">
-          <img src="../../../resources/img/new_img/rabbit.png" alt="" />
+          <img
+            src="../../../resources/img/new_img/rabbit.png"
+            alt=""
+            :style="{ display: rabbitPos[0] ? 'block' : 'none' }"
+          />
         </div>
         <!-- 1번째줄 -->
         <div
@@ -12,7 +53,14 @@
           style="position: relative; top: 24px"
         >
           <!-- 1 -->
-          <div class="race-btn" @click="goToPage('Chapter1')">
+          <div class="race-btn" @click="[storeLearningId(labeledItems[0])]">
+            <div class="rabbit-running">
+              <img
+                src="../../../resources/img/new_img/rabbit.png"
+                alt=""
+                :style="{ display: rabbitPos[1] ? 'block' : 'none' }"
+              />
+            </div>
             <button
               class="popTxt"
               v-for="(item, index) in items"
@@ -23,29 +71,36 @@
               <div class="image-container">
                 <img
                   src="../../../resources/img/new_img/ai_board/racebtn_1.png"
+                  alt="Race Button"
+                  class="base-img"
                 />
                 <img
+                  v-if="rabbitCompl[1]"
                   src="../../../resources/img/new_img/icon/clear_img.png"
-                  :style="{
-                    display: item.isSecondImageVisible ? 'block' : 'none',
-                  }"
+                  alt="Clear Icon"
                   class="clear-img"
                 />
               </div>
-              <p :class="!item.isSecondImageVisible ? 'before-clear' : 'clear'">
-                <img
-                  v-if="item.isSecondImageVisible"
-                  src="../../../resources/img/new_img/icon/complete_icon.png"
-                  alt="Complete Icon"
-                  class="complete-icon"
-                />
-                지문1
-              </p>
             </button>
-            <!-- <p>지문1</p> -->
+            <p :class="!rabbitCompl[1] ? 'before-clear' : 'clear'">
+              <img
+                v-if="rabbitCompl[1]"
+                src="../../../resources/img/new_img/icon/complete_icon.png"
+                alt="Complete Icon"
+                class="complete-icon"
+              />
+              {{ labeledItems[0].label }}
+            </p>
           </div>
           <!-- 2 -->
-          <div class="race-btn" @click="goToPage('Chapter2')">
+          <div class="race-btn" @click="[storeLearningId(labeledItems[1])]">
+            <div class="rabbit-running">
+              <img
+                src="../../../resources/img/new_img/rabbit.png"
+                alt=""
+                :style="{ display: rabbitPos[2] ? 'block' : 'none' }"
+              />
+            </div>
             <button
               class="popTxt"
               v-for="(item, index) in items"
@@ -56,67 +111,85 @@
               <div class="image-container">
                 <img
                   src="../../../resources/img/new_img/ai_board/racebtn_2.png"
+                  alt="Race Button"
+                  class="base-img"
                 />
                 <img
+                  v-if="rabbitCompl[2]"
                   src="../../../resources/img/new_img/icon/clear_img.png"
-                  :style="{
-                    display: item.isSecondImageVisible ? 'block' : 'none',
-                  }"
+                  alt="Clear Icon"
                   class="clear-img"
                   style="position: absolute; left: 16px"
                 />
               </div>
-              <p :class="!item.isSecondImageVisible ? 'before-clear' : 'clear'">
-                <img
-                  v-if="item.isSecondImageVisible"
-                  src="../../../resources/img/new_img/icon/complete_icon.png"
-                  alt="Complete Icon"
-                  class="complete-icon"
-                />
-                단어장
-              </p>
             </button>
-            <!-- <p>단어장</p> -->
+            <p :class="!rabbitCompl[2] ? 'before-clear' : 'clear'">
+              <img
+                v-if="rabbitCompl[2]"
+                src="../../../resources/img/new_img/icon/complete_icon.png"
+                alt="Complete Icon"
+                class="complete-icon"
+              />
+              {{ labeledItems[1].label }}
+            </p>
           </div>
         </div>
-        <!-- 2번째 줄 -->
+        <!-- 2번째줄 -->
         <div class="lcon flex justify-between mb5">
           <!-- 7 -->
-          <div class="race-btn" @click="goToPage('Chapter7')">
+          <div class="race-btn" @click="[storeLearningId(labeledItems[6])]">
+            <div class="rabbit-running" style="top: -71px">
+              <img
+                src="../../../resources/img/new_img/rabbit.png"
+                alt=""
+                :style="{ display: rabbitPos[7] ? 'block' : 'none' }"
+              />
+            </div>
             <button
               class="popTxt"
               v-for="(item, index) in items"
               :key="index"
               @click="toggleImage(index)"
               data-num="7"
+              style="top: -53px; position: relative"
             >
               <div class="image-container">
                 <img
                   src="../../../resources/img/new_img/ai_board/racebtn_7.png"
+                  alt="Race Button"
+                  class="base-img"
                 />
                 <img
+                  v-if="rabbitCompl[7]"
                   src="../../../resources/img/new_img/icon/clear_img.png"
-                  :style="{
-                    display: item.isSecondImageVisible ? 'block' : 'none',
-                  }"
+                  alt="Clear Icon"
                   class="clear-img"
                   style="position: absolute; top: 85px"
                 />
               </div>
-              <p :class="!item.isSecondImageVisible ? 'before-clear' : 'clear'">
-                <img
-                  v-if="item.isSecondImageVisible"
-                  src="../../../resources/img/new_img/icon/complete_icon.png"
-                  alt="Complete Icon"
-                  class="complete-icon"
-                />
-                문제1
-              </p>
             </button>
-            <!-- <p>문제1</p> -->
+            <p
+              :class="!rabbitCompl[7] ? 'before-clear' : 'clear'"
+              style="position: relative; top: -53px; left: -10px"
+            >
+              <img
+                v-if="rabbitCompl[7]"
+                src="../../../resources/img/new_img/icon/complete_icon.png"
+                alt="Complete Icon"
+                class="complete-icon"
+              />
+              {{ labeledItems[6].label }}
+            </p>
           </div>
           <!-- 6 -->
-          <div class="race-btn" @click="goToPage('Chapter6')">
+          <div class="race-btn" @click="[storeLearningId(labeledItems[5])]">
+            <div class="rabbit-running">
+              <img
+                src="../../../resources/img/new_img/rabbit.png"
+                alt=""
+                :style="{ display: rabbitPos[6] ? 'block' : 'none' }"
+              />
+            </div>
             <button
               class="popTxt"
               v-for="(item, index) in items"
@@ -127,67 +200,80 @@
               <div class="image-container">
                 <img
                   src="../../../resources/img/new_img/ai_board/racebtn_6.png"
+                  alt="Race Button"
+                  class="base-img"
                 />
                 <img
+                  v-if="rabbitCompl[6]"
                   src="../../../resources/img/new_img/icon/clear_img.png"
-                  :style="{
-                    display: item.isSecondImageVisible ? 'block' : 'none',
-                  }"
+                  alt="Clear Icon"
                   class="clear-img"
                 />
               </div>
-              <p :class="!item.isSecondImageVisible ? 'before-clear' : 'clear'">
-                <img
-                  v-if="item.isSecondImageVisible"
-                  src="../../../resources/img/new_img/icon/complete_icon.png"
-                  alt="Complete Icon"
-                  class="complete-icon"
-                />
-                단어장
-              </p>
             </button>
-            <!-- <p>단어장</p> -->
+            <p :class="!rabbitCompl[6] ? 'before-clear' : 'clear'">
+              <img
+                v-if="rabbitCompl[6]"
+                src="../../../resources/img/new_img/icon/complete_icon.png"
+                alt="Complete Icon"
+                class="complete-icon"
+              />
+              {{ labeledItems[5].label }}
+            </p>
           </div>
           <!-- 5 -->
           <div
             class="race-btn"
-            @click="goToPage('Chapter5')"
+            @click="[storeLearningId(labeledItems[4])]"
             style="position: relative; left: -31px"
           >
+            <div class="rabbit-running">
+              <img
+                src="../../../resources/img/new_img/rabbit.png"
+                alt=""
+                :style="{ display: rabbitPos[5] ? 'block' : 'none' }"
+              />
+            </div>
             <button
               class="popTxt"
               v-for="(item, index) in items"
               :key="index"
               @click="toggleImage(index)"
               data-num="5"
-              style="position: absolute; top: -77px; left: 30px"
             >
               <div class="image-container">
                 <img
                   src="../../../resources/img/new_img/ai_board/racebtn_5.png"
+                  alt="Race Button"
+                  class="base-img"
                 />
                 <img
+                  v-if="rabbitCompl[5]"
                   src="../../../resources/img/new_img/icon/clear_img.png"
-                  :style="{
-                    display: item.isSecondImageVisible ? 'block' : 'none',
-                  }"
+                  alt="Clear Icon"
                   class="clear-img"
                 />
               </div>
-              <p :class="!item.isSecondImageVisible ? 'before-clear' : 'clear'">
-                <img
-                  v-if="item.isSecondImageVisible"
-                  src="../../../resources/img/new_img/icon/complete_icon.png"
-                  alt="Complete Icon"
-                  class="complete-icon"
-                />
-                지문2
-              </p>
             </button>
-            <!-- <p>지문2</p> -->
+            <p :class="!rabbitCompl[5] ? 'before-clear' : 'clear'">
+              <img
+                v-if="rabbitCompl[5]"
+                src="../../../resources/img/new_img/icon/complete_icon.png"
+                alt="Complete Icon"
+                class="complete-icon"
+              />
+              {{ labeledItems[4].label }}
+            </p>
           </div>
           <!-- 4 -->
-          <div class="race-btn" @click="goToPage('Chapter4')">
+          <div class="race-btn" @click="[storeLearningId(labeledItems[3])]">
+            <div class="rabbit-running">
+              <img
+                src="../../../resources/img/new_img/rabbit.png"
+                alt=""
+                :style="{ display: rabbitPos[4] ? 'block' : 'none' }"
+              />
+            </div>
             <button
               class="popTxt"
               v-for="(item, index) in items"
@@ -198,70 +284,83 @@
               <div class="image-container">
                 <img
                   src="../../../resources/img/new_img/ai_board/racebtn_4.png"
+                  alt="Race Button"
+                  class="base-img"
                 />
                 <img
+                  v-if="rabbitCompl[4]"
                   src="../../../resources/img/new_img/icon/clear_img.png"
-                  :style="{
-                    display: item.isSecondImageVisible ? 'block' : 'none',
-                  }"
+                  alt="Clear Icon"
                   class="clear-img"
                 />
               </div>
-              <p :class="!item.isSecondImageVisible ? 'before-clear' : 'clear'">
-                <img
-                  v-if="item.isSecondImageVisible"
-                  src="../../../resources/img/new_img/icon/complete_icon.png"
-                  alt="Complete Icon"
-                  class="complete-icon"
-                />
-                문제2
-              </p>
             </button>
-            <!-- <p>문제2</p> -->
+            <p :class="!rabbitCompl[4] ? 'before-clear' : 'clear'">
+              <img
+                v-if="rabbitCompl[4]"
+                src="../../../resources/img/new_img/icon/complete_icon.png"
+                alt="Complete Icon"
+                class="complete-icon"
+              />
+              {{ labeledItems[3].label }}
+            </p>
           </div>
           <!-- 3 -->
           <div
             class="race-btn"
-            @click="goToPage('Chapter3')"
+            @click="[storeLearningId(labeledItems[2])]"
             style="right: 12px"
           >
+            <div class="rabbit-running">
+              <img
+                src="../../../resources/img/new_img/rabbit.png"
+                alt=""
+                :style="{ display: rabbitPos[3] ? 'block' : 'none' }"
+              />
+            </div>
             <button
               class="popTxt"
               v-for="(item, index) in items"
               :key="index"
               @click="toggleImage(index)"
               data-num="3"
-              style="top: -46px; position: relative"
             >
               <div class="image-container">
                 <img
                   src="../../../resources/img/new_img/ai_board/racebtn_3.png"
+                  alt="Race Button"
+                  class="base-img"
                 />
                 <img
+                  v-if="rabbitCompl[3]"
                   src="../../../resources/img/new_img/icon/clear_img.png"
-                  :style="{
-                    display: item.isSecondImageVisible ? 'block' : 'none',
-                  }"
+                  alt="Clear Icon"
                   class="clear-img"
                 />
               </div>
-              <p :class="!item.isSecondImageVisible ? 'before-clear' : 'clear'">
-                <img
-                  v-if="item.isSecondImageVisible"
-                  src="../../../resources/img/new_img/icon/complete_icon.png"
-                  alt="Complete Icon"
-                  class="complete-icon"
-                />
-                문제1
-              </p>
             </button>
-            <!-- <p>문제1</p> -->
+            <p :class="!rabbitCompl[3] ? 'before-clear' : 'clear'">
+              <img
+                v-if="rabbitCompl[3]"
+                src="../../../resources/img/new_img/icon/complete_icon.png"
+                alt="Complete Icon"
+                class="complete-icon"
+              />
+              {{ labeledItems[2].label }}
+            </p>
           </div>
         </div>
-        <!-- 3번째 줄 -->
+        <!-- 3번째줄 -->
         <div class="rcon flex" style="position: relative; top: -23px">
           <!-- 8 -->
-          <div class="race-btn" @click="goToPage('Chapter8')">
+          <div class="race-btn" @click="[storeLearningId(labeledItems[7])]">
+            <div class="rabbit-running">
+              <img
+                src="../../../resources/img/new_img/rabbit.png"
+                alt=""
+                :style="{ display: rabbitPos[8] ? 'block' : 'none' }"
+              />
+            </div>
             <button
               class="popTxt"
               v-for="(item, index) in items"
@@ -272,29 +371,36 @@
               <div class="image-container">
                 <img
                   src="../../../resources/img/new_img/ai_board/racebtn_8.png"
+                  alt="Race Button"
+                  class="base-img"
                 />
                 <img
+                  v-if="rabbitCompl[8]"
                   src="../../../resources/img/new_img/icon/clear_img.png"
-                  :style="{
-                    display: item.isSecondImageVisible ? 'block' : 'none',
-                  }"
+                  alt="Clear Icon"
                   class="clear-img"
                 />
               </div>
-              <p :class="!item.isSecondImageVisible ? 'before-clear' : 'clear'">
-                <img
-                  v-if="item.isSecondImageVisible"
-                  src="../../../resources/img/new_img/icon/complete_icon.png"
-                  alt="Complete Icon"
-                  class="complete-icon"
-                />
-                중간 평가
-              </p>
             </button>
-            <!-- <p class="long">중간 평가</p> -->
+            <p :class="!rabbitCompl[8] ? 'before-clear' : 'clear'">
+              <img
+                v-if="rabbitCompl[8]"
+                src="../../../resources/img/new_img/icon/complete_icon.png"
+                alt="Complete Icon"
+                class="complete-icon"
+              />
+              {{ labeledItems[7].label }}
+            </p>
           </div>
           <!-- 9 -->
-          <div class="race-btn" @click="goToPage('Chapter9')">
+          <div class="race-btn" @click="[storeLearningId(labeledItems[8])]">
+            <div class="rabbit-running">
+              <img
+                src="../../../resources/img/new_img/rabbit.png"
+                alt=""
+                :style="{ display: rabbitPos[9] ? 'block' : 'none' }"
+              />
+            </div>
             <button
               class="popTxt"
               v-for="(item, index) in items"
@@ -305,29 +411,36 @@
               <div class="image-container">
                 <img
                   src="../../../resources/img/new_img/ai_board/racebtn_9.png"
+                  alt="Race Button"
+                  class="base-img"
                 />
                 <img
+                  v-if="rabbitCompl[9]"
                   src="../../../resources/img/new_img/icon/clear_img.png"
-                  :style="{
-                    display: item.isSecondImageVisible ? 'block' : 'none',
-                  }"
+                  alt="Clear Icon"
                   class="clear-img"
                 />
               </div>
-              <p :class="!item.isSecondImageVisible ? 'before-clear' : 'clear'">
-                <img
-                  v-if="item.isSecondImageVisible"
-                  src="../../../resources/img/new_img/icon/complete_icon.png"
-                  alt="Complete Icon"
-                  class="complete-icon"
-                />
-                지문3
-              </p>
             </button>
-            <!-- <p>지문3</p> -->
+            <p :class="!rabbitCompl[9] ? 'before-clear' : 'clear'">
+              <img
+                v-if="rabbitCompl[9]"
+                src="../../../resources/img/new_img/icon/complete_icon.png"
+                alt="Complete Icon"
+                class="complete-icon"
+              />
+              {{ labeledItems[8].label }}
+            </p>
           </div>
           <!-- 10 -->
-          <div class="race-btn" @click="goToPage('Chapter10')">
+          <div class="race-btn" @click="[storeLearningId(labeledItems[9])]">
+            <div class="rabbit-running">
+              <img
+                src="../../../resources/img/new_img/rabbit.png"
+                alt=""
+                :style="{ display: rabbitPos[10] ? 'block' : 'none' }"
+              />
+            </div>
             <button
               class="popTxt"
               v-for="(item, index) in items"
@@ -338,63 +451,83 @@
               <div class="image-container">
                 <img
                   src="../../../resources/img/new_img/ai_board/racebtn_10.png"
+                  alt="Race Button"
+                  class="base-img"
                 />
                 <img
+                  v-if="rabbitCompl[10]"
                   src="../../../resources/img/new_img/icon/clear_img.png"
-                  :style="{
-                    display: item.isSecondImageVisible ? 'block' : 'none',
-                  }"
+                  alt="Clear Icon"
                   class="clear-img"
                 />
               </div>
-              <p :class="!item.isSecondImageVisible ? 'before-clear' : 'clear'">
-                <img
-                  v-if="item.isSecondImageVisible"
-                  src="../../../resources/img/new_img/icon/complete_icon.png"
-                  alt="Complete Icon"
-                  class="complete-icon"
-                />
-                단어장
-              </p>
             </button>
-            <!-- <p>단어장</p> -->
+            <p :class="!rabbitCompl[10] ? 'before-clear' : 'clear'">
+              <img
+                v-if="rabbitCompl[10]"
+                src="../../../resources/img/new_img/icon/complete_icon.png"
+                alt="Complete Icon"
+                class="complete-icon"
+              />
+              {{ labeledItems[9].label }}
+            </p>
           </div>
           <!-- 11 -->
-          <div class="race-btn">
+          <div class="race-btn" @click="[storeLearningId(labeledItems[10])]">
+            <div class="rabbit-running">
+              <img
+                src="../../../resources/img/new_img/rabbit.png"
+                alt=""
+                :style="{ display: rabbitPos[11] ? 'block' : 'none' }"
+              />
+            </div>
             <button
               class="popTxt"
               v-for="(item, index) in items"
               :key="index"
-              @click="toggleImage(index)"
+              @click="toggleImageAndShowPopup(index, '11')"
               data-num="11"
             >
               <div class="image-container">
                 <img
                   src="../../../resources/img/new_img/ai_board/racebtn_11.png"
+                  alt="Race Button"
+                  class="base-img"
                 />
                 <img
+                  v-if="rabbitCompl[11]"
                   src="../../../resources/img/new_img/icon/clear_img.png"
-                  :style="{
-                    display: item.isSecondImageVisible ? 'block' : 'none',
-                  }"
+                  alt="Clear Icon"
                   class="clear-img"
                 />
               </div>
-              <p :class="!item.isSecondImageVisible ? 'before-clear' : 'clear'">
-                <img
-                  v-if="item.isSecondImageVisible"
-                  src="../../../resources/img/new_img/icon/complete_icon.png"
-                  alt="Complete Icon"
-                  class="complete-icon"
-                />
-                최종 평가
-              </p>
             </button>
-            <!-- <p class="long">최종 평가</p> -->
+            <p :class="!rabbitCompl[11] ? 'before-clear' : 'clear'">
+              <img
+                v-if="rabbitCompl[11]"
+                src="../../../resources/img/new_img/icon/complete_icon.png"
+                alt="Complete Icon"
+                class="complete-icon"
+              />
+              {{ labeledItems[10].label }}
+            </p>
           </div>
         </div>
-        <div class="rabbit-end" @click="ShowPopup">
-          <!-- <img src="../../../resources/img/img138_72s.png" alt="" /> -->
+        <div class="race-btn">
+          <div class="rabbit-running" style="display: flex">
+            <img
+              class="rabbit-end"
+              src="../../../resources/img/new_img/rabbit.png"
+              alt=""
+              :style="{ display: rabbitEnd ? 'block' : 'none' }"
+            />
+            <img
+              class="fireworks-end"
+              src="../../../resources/img/fireworks.gif"
+              alt=""
+              :style="{ display: rabbitEnd ? 'block' : 'none' }"
+            />
+          </div>
         </div>
       </div>
 
@@ -432,7 +565,6 @@
           </div>
         </div>
       </div>
-
       <!-- 카메라 모달 -->
       <article v-show="showCameraModal" class="popup-wrap">
         <div class="popup-box" style="top: 500px; left: 500px">
@@ -472,6 +604,7 @@
         </div>
       </article>
     </div>
+
     <div class="complete-wrap myphoto">
       <button
         class="login-btn mt10"
@@ -487,6 +620,7 @@
         <p>학습 종료하기</p>
       </button>
       <h2 class="mb40 mt10">이 단원을 끝낸 친구들</h2>
+
       <article class="flex-column">
         <div class="flex-row">
           <div class="flex" style="gap: 5px; flex-wrap: wrap">
@@ -508,9 +642,8 @@
         </div>
       </article>
       <article class="popup-wrap" v-show="searchOpen">
-        <div class="popup-box">
+        <div class="popup-box" style="top: 50%">
           <div class="flex mb10 justify-between">
-            <p class="popup-title">알림</p>
             <button type="button" class="popup-close-btn" @click="closeModal">
               <svg-icon
                 type="mdi"
@@ -520,24 +653,46 @@
             </button>
           </div>
           <div class="box">
-            <div style="width: 910px">
-              <img src="../../../resources/img/img140_747s.png" alt="" />
+            <div style="width: 910px; height: 680px">
+              <img
+                :src="selectedImage.image"
+                alt="Image"
+                @error="onImageError"
+                reloadable="true"
+              />
             </div>
           </div>
           <div class="flex justify-between mt20">
             <div class="text flex">
-              <p class="title2 date ml30">2024-08-06</p>
+              <p class="title2 date ml30">{{ selectedImage.date }}</p>
               <span class="title1 ml30"
-                >1단원을 마친 <em class="yellow">가나다</em>친구</span
+                >{{ selectedImage.unit }}을 마친
+                <em class="yellow">{{ selectedImage.name }}</em
+                >친구</span
               >
             </div>
             <div class="title2 flex align-center" style="gap: 10px">
               <svg-icon
+                v-if="!isHeartFilled"
+                type="mdi"
+                :path="mdiHeartOutline"
+                @click="
+                  toggleHeart(selectedImage.heart, selectedImage.fileMngId)
+                "
+                style="color: #ffba08; cursor: pointer"
+              ></svg-icon>
+              <svg-icon
+                v-if="isHeartFilled"
                 type="mdi"
                 :path="mdiHeart"
-                style="color: #ffba08"
+                @click="
+                  toggleHeart(selectedImage.heart, selectedImage.fileMngId)
+                "
+                style="color: #ffba08; cursor: pointer"
               ></svg-icon>
-              <p><em class="yellow">1</em></p>
+              <p>
+                <em class="yellow">{{ selectedImage.heart }}</em>
+              </p>
             </div>
           </div>
         </div>
@@ -549,9 +704,12 @@
 <script>
 import SvgIcon from "@jamescoyle/vue-icon";
 import { mdiMagnify, mdiHeart, mdiWindowClose } from "@mdi/js";
+import { mdiHeartOutline } from "@mdi/js";
 import axios from "axios";
-
-//은진
+import { call } from "file-loader";
+import { name } from "file-loader";
+import { mapGetters } from "vuex";
+import { mapActions } from "vuex";
 
 export default {
   data() {
@@ -565,8 +723,10 @@
           isSecondImageVisible: false,
         },
       ],
+      mdiMagnify: mdiMagnify,
       mdiWindowClose: mdiWindowClose,
       mdiHeart: mdiHeart,
+      mdiHeartOutline: mdiHeartOutline,
       showModal: false,
       searchOpen: false, // 사진 상세보기 모달창
       searchOpen2: false, // 단원 마친 후, 사진 촬영 여부 선택 모달창
@@ -575,11 +735,147 @@
       photo: null, //캡쳐 사진
       stream: null,
 
-      isHidden: false,
+      roadmapData: [],
+      labeledItems: [],
+
+      problemCounter: 0,
+      wordCounter: 0,
+      textCounter: 0,
+      evalCounter: 0,
+      book_id: null,
+      unit_id: null,
+
+      schedules: [],
+      nowSchedule: "",
+      state: "",
+      rabbitPos: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+      rabbitCompl: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+      rabbitEnd: false,
+
+      titleUnitName: "",
+      titleBookName: "",
+
       images: [],
+
+      selectedImage: [
+        {
+          image: "",
+          unit: "",
+          date: "",
+          name: "",
+          heart: "",
+          fileMngId: "",
+        },
+      ],
+
+      isHeartFilled: false, // 하트가 채워졌는지 여부
+
+      problemType: null,
+      wordBookType: null,
+      wordContentList: [],
     };
   },
   methods: {
+    toggleHeart(heart, fileMngId) {
+      this.isHeartFilled = !this.isHeartFilled; // 하트 상태 토글
+
+      if (this.isHeartFilled) var calHeart = heart + 1;
+      else var calHeart = heart - 1;
+
+      // 하트 수를 증가시키기 위한 API 요청
+      axios
+        .post("/photo/likeUpdate.json", {
+          likeData: calHeart,
+          fileMngId: fileMngId,
+        })
+        .then((response) => {
+          this.selectedImage.heart = calHeart;
+        })
+        .catch((error) => {
+          console.error("Error updating heart:", error);
+        });
+    },
+    checkAndFetchData() {
+      console.log("받은 Book ID:", this.getBookId);
+      console.log("받은 Unit ID:", this.getUnitId);
+      const book_id = this.getBookId;
+      const unit_id = this.getUnitId;
+
+      if (!book_id || !unit_id) {
+        console.error("book_id 또는 unit_id가 설정되지 않았습니다.");
+        this.state = "notRegistered";
+        return;
+      }
+      this.fetchImage(unit_id);
+      this.fetchSchedule(unit_id, book_id);
+      this.fetchRoadmapData(unit_id, book_id);
+      this.fetchRabbit();
+
+      this.unit_id = unit_id;
+      this.book_id = book_id;
+
+      if (this.$route.query.reCapture == "true") {
+        this.openCameraModal();
+      }
+
+      this.searchStdId();
+    },
+
+    //은진
+    buttonSearch(image) {
+      this.isHeartFilled = false;
+      this.selectedImage.name = image.stdId;
+      this.selectedImage.image = image.url;
+      this.selectedImage.unit = this.titleUnitName;
+      this.selectedImage.date = image.fileRegDate.split(" ")[0];
+      this.selectedImage.heart = image.likeData;
+      this.selectedImage.fileMngId = image.fileMngId;
+      this.searchOpen = true;
+    },
+    fetchImage(unit_id) {
+      axios({
+        url: "/photo/photoUnitList.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          unitId: unit_id,
+          sclsId: "1",
+        },
+      })
+        .then((response) => {
+          this.file = response.data;
+
+          const findFilePromises = this.file.map((f) =>
+            this.findFile(f.file_mng_id)
+          );
+
+          return Promise.all(findFilePromises);
+        })
+        .then((fileResults) => {
+          this.images = this.file
+            .map((file, index) => {
+              const result = fileResults[index];
+              if (result) {
+                return {
+                  url: "http://165.229.169.113:9080/" + `${result.fileRpath}`,
+                  fileId: result.fileId,
+                  fileNm: result.fileNm,
+                  fileRegDate: result.regDt,
+                  likeData: file.like_data,
+                  stdId: file.user_nm,
+                  fileMngId: result.fileMngId,
+                };
+              }
+              return null;
+            })
+            .filter((image) => image !== null);
+        })
+        .catch((error) => {
+          console.error("Error fetching images:", error);
+        });
+    },
     async findFile(file_mng_id) {
       try {
         const res = await axios({
@@ -598,47 +894,6 @@
         return null;
       }
     },
-    fetchImage() {
-      axios({
-        url: "/photo/photoUnitList.json",
-        method: "post",
-        headers: {
-          "Content-Type": "application/json; charset=UTF-8",
-        },
-        data: {
-          unitId: "UNIT_000000000000001", // 수정해야함
-          sclsId: "1",
-        },
-      })
-        .then((response) => {
-          this.file_mng_id = response.data;
-
-          const findFilePromises = this.file_mng_id.map((id) =>
-            this.findFile(id.file_mng_id)
-          );
-
-          return Promise.all(findFilePromises);
-        })
-        .then((fileResults) => {
-          // Format file results to include image URL
-          this.images = fileResults
-            .map((file) => {
-              if (file) {
-                return {
-                  url: "http://165.229.169.113:9080/" + `${file.fileRpath}`,
-                  fileId: file.fileId,
-                  fileNm: file.fileNm,
-                  // Add any other properties you need here
-                };
-              }
-              return null;
-            })
-            .filter((image) => image !== null);
-        })
-        .catch((error) => {
-          console.error("Error fetching images:", error);
-        });
-    },
     goToPageImg(page) {
       const canvas = document.querySelector("canvas");
       const dataURL = canvas.toDataURL("image/png");
@@ -648,18 +903,248 @@
         query: { image: encodeURIComponent(dataURL) },
       });
     },
+
+    fetchRabbit() {
+      for (var i = 0; i < 12; i++) {
+        this.rabbitPos[i] = false;
+      }
+
+      if (this.$route.query.value) {
+        this.rabbitPos[parseInt(this.$route.query.value, 10) + 1] = true;
+        for (var i = 0; i < this.$route.query.value; i++) {
+          this.rabbitCompl[i + 1] = true;
+        }
+
+        if (this.$route.query.value === "11") {
+          this.rabbitEnd = true;
+          setTimeout(() => {
+            this.searchOpen2 = true;
+          }, 1000);
+        }
+      } else this.rabbitPos[0] = true;
+    },
+    fetchSchedule(unit_id, book_id) {
+      axios({
+        url: "/schedule/selectSchedule.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          stdId: "2",
+        },
+      })
+        .then((response) => {
+          this.schedules = response.data;
+
+          if (this.schedules.length == 0) {
+            this.state = "notRegistered";
+          } else {
+            const allFinished = this.schedules.every(
+              (schedule) => schedule.finish === "T"
+            );
+            if (allFinished) {
+              this.state = "finish";
+            } else {
+              this.nowSchedule = this.schedules.find(
+                (schedule) =>
+                  schedule.finish === null || schedule.finish === "F"
+              );
+              if (this.nowSchedule) {
+                this.fetchRoadmapData(unit_id, book_id);
+                this.state = "studying";
+              } else {
+                this.state = "notRegistered";
+              }
+            }
+          }
+        })
+        .catch((error) => {
+          console.error("Error fetching roadmap data:", error);
+        });
+    },
     finishSchedule() {
-      this.isHidden = true;
+      axios({
+        url: "/schedule/scheduleUpdate.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          scheduleId: this.nowSchedule.schdl_id,
+          finish: "T",
+        },
+      })
+        .then((response) => {
+          const nextSchedule = this.schedules.find(
+            (schedule) => schedule.schdl_id > this.nowSchedule.schdl_id
+          );
+          alert("학습을 완료했습니다!");
+
+          if (nextSchedule) {
+            this.nowSchedule = nextSchedule;
+            this.fetchSchedule(nextSchedule.unit_id, nextSchedule.book_id);
+            this.fetchRoadmapData(nextSchedule.unit_id, nextSchedule.book_id);
+            this.$router.push({
+              name: "Dashboard",
+              query: {
+                unit_id: nextSchedule.unit_id,
+                book_id: nextSchedule.book_id,
+              },
+            });
+          } else {
+            alert("모든 학습을 완료했습니다!");
+            this.state = "finish";
+          }
+        })
+        .catch((error) => {
+          console.error("Error updating schedule:", error);
+        });
+    },
+    fetchRoadmapData(unit_id, book_id) {
+      axios({
+        url: "/aiLearning/aiLearningDetail.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          unit_id: "UNIT_0000000000000301",
+          user_id: "USID_000000000000002"
+        },
+      })
+        .then((response) => {
+          if (response.data.length != 0) {
+            this.roadmapData = response.data;
+            this.titleUnitName = this.roadmapData[0].unit_nm;
+            this.titleBookName = this.roadmapData[0].book_nm;
+            this.labeledItems = this.processedRoadmap;
+            console.log(this.roadmapData, this.labeledItems);
+          } else {
+            this.state = "noProblem";
+          }
+        })
+        .catch((error) => {
+          this.state = "noProblem";
+          console.error("Error fetching roadmap data:", error);
+        });
     },
     toggleImage(index) {
       this.items[index].isSecondImageVisible =
         !this.items[index].isSecondImageVisible;
     },
     ShowPopup() {
-      this.searchOpen2 = true;
+      this.searchOpen2 = true; // 촬영 여부 묻는 모달창 열기
     },
     goToPage(page) {
+      // const { unit_id, book_id } = this.$route.query;
+      // this.$router.push({ name: page, query: { unit_id, book_id } });
       this.$router.push({ name: page });
+    },
+    goToPage2(page, unit_id) {
+      this.$router.push({
+        name: page,
+        query: {
+          unit_id: unit_id,
+        },
+      });
+    },
+
+    storeLearningId(labeledItems) {
+      this.$store.dispatch("updateLearningData", labeledItems);
+      this.$store.dispatch("updateLearningId", labeledItems.learning_id); // 단어장에 사용중..
+
+      console.log("레이블된 아이템: ", labeledItems);
+      console.log(labeledItems.prblm_type_id)
+      if (labeledItems.label.startsWith("문제")) {
+        // this.handleProblemDetail(this.$store.getters.currentLearningId);
+        this.handleProblemDetail(labeledItems);
+        this.goToPage(this.problemType);
+      }
+    },
+    
+    // 평가 정보를 통해서 관련 문제 가져오기
+    fetchEvalData(evaldata) {
+      axios({
+        url: "/evalProblem/selectEvalProblem.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          evalId: evaldata.learning_id,
+        },
+      })
+        .then((response) => {
+          const payload = {
+            learning_id: response.data,
+            label: evaldata.label,
+            seqNum: evaldata.seqNum,
+          };
+
+          // Vuex 뮤테이션 호출
+          this.$store.commit("setLearningData", payload);
+          this.handleProblemDetail(response.data[0]);
+          this.goToPage(this.problemType);
+        })
+        .catch((error) => {
+          console.error("fetchData - error: ", error);
+          return [];
+        });
+    },
+
+    handleProblemDetail(item) {
+      console.log('item ', item);
+      if (item.prblm_type_id === "prblm_type_001") {
+        this.problemType = "Chapter3";
+      } else if (item.prblm_type_id === "prblm_type_002") {
+        this.problemType = "Chapter3_1";
+      } else if (item.prblm_type_id === "prblm_type_003") {
+        this.problemType = "Chapter3_2";
+      } else if (item.prblm_type_id === "prblm_type_004") {
+        this.problemType = "Chapter3_3";
+      } else if (item.prblm_type_id === "prblm_type_005") {
+        this.problemType = "Chapter3_3_1";
+      } else if (item.prblm_type_id === "prblm_type_006") {
+        console.log(item);
+        this.problemType = "Chapter3_4";
+      } else if (item.prblm_type_id === "prblm_type_007") {
+        this.problemType = "Chapter3_5";
+      } else if (item.prblm_type_id === "prblm_type_008") {
+        this.problemType = "Chapter3_6";
+      } else if (item.prblm_type_id === "prblm_type_009") {
+        this.problemType = "Chapter3_7";
+      } else if (item.prblm_type_id === "prblm_type_010") {
+        this.problemType = "Chapter3_8";
+      } else if (item.prblm_type_id === "prblm_type_011") {
+        this.problemType = "Chapter3_9";
+      } else if (item.prblm_type_id === "prblm_type_012") {
+        this.problemType = "Chapter3_10";
+      } else if (item.prblm_type_id === "prblm_type_013") {
+        this.problemType = "Chapter3_11";
+      } else if (item.prblm_type_id === "prblm_type_014") {
+        this.problemType = "Chapter3_12";
+      } else if (item.prblm_type_id === "prblm_type_015") {
+        this.problemType = "Chapter3_13";
+      } else if (item.prblm_type_id === "prblm_type_016") {
+        this.problemType = "Chapter3_14";
+      } else if (item.prblm_type_id === "prblm_type_017") {
+        this.problemType = "Chapter3_15";
+      } else if (item.prblm_type_id === "prblm_type_018") {
+        this.problemType = "Chapter2_8";
+      } else if (item.prblm_type_id === "prblm_type_019") {
+        this.problemType = "Chapter2_7";
+      } else if (item.prblm_type_id === "prblm_type_020") {
+        this.problemType = "Chapter2_5";
+      } else if (item.prblm_type_id === "prblm_type_021") {
+        this.problemType = "Chapter2_6";
+      } else if (item.prblm_type_id === "prblm_type_022") {
+        this.problemType = "Chapter2_10";
+      } else if (item.prblm_type_id === "prblm_type_023") {
+        this.problemType = "Chapter2_11";
+      } else if (item.prblm_type_id === "prblm_type_024") {
+        this.problemType = "Chapter2_13";
+      }
     },
     openCameraModal() {
       this.closeModal();
@@ -674,11 +1159,11 @@
         })
         .catch((error) => {
           console.log("error>>>>>>>>", error);
+          alert("웹캠이 필요한 기능입니다!");
+          this.closeModal(); // 모달창을 닫음
         });
     },
     closeModal() {
-      //웹캠 및 모든 팝업 닫기
-      // this.showModal = false;
       this.searchOpen = false;
       this.searchOpen2 = false;
       this.showCameraModal = false;
@@ -693,7 +1178,6 @@
       }
     },
 
-    //은진
     onVideoLoaded() {
       const video = this.$refs.modalVideoElement;
       const canvas = this.$refs.canvas;
@@ -734,23 +1218,45 @@
         );
 
         // 사진 저장 함수 호출
-        // this.savePhoto('PhotoEdit');
         const dataURL = canvas.toDataURL("image/png");
         this.$router.push({
           name: "PhotoEdit",
-          query: { image: encodeURIComponent(dataURL) },
+          query: {
+            image: encodeURIComponent(dataURL),
+            unit_id: this.unit_id,
+            book_id: this.book_id,
+          },
         });
       };
     },
 
-    buttonSearch() {
-      this.searchOpen = true;
-    },
     buttonSearch2() {
       this.searchOpen2 = true;
     },
     closeBtn() {
       this.searchOpen = false;
+    },
+
+    // 유저 아이디를 unit 아이디를 통해 가져오기
+    searchStdId() {
+      const userInfo = this.$store.getters.getUserInfo;
+      axios({
+        url: "/userclass/searchStdId.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          userId: userInfo.userId,
+          unitId: this.unit_id,
+        },
+      })
+        .then((response) => {
+          this.$store.commit("setStdId", response.data);
+        })
+        .catch((err) => {
+          console.log("지문 에러 : ", err);
+        });
     },
   },
   components: {
@@ -758,19 +1264,47 @@
   },
   mounted() {
     console.log("main mounted");
-
-    this.onVideoLoaded();
-    this.fetchImage();
-
-    if (this.$route.query.reCapture == "true") {
-      this.openCameraModal();
-    }
+    this.checkAndFetchData();
+    // const { book_id, unit_id } = this.$route.query;
   },
-  computed() {},
+  watch: {
+    getBookId(newBookId) {
+      this.checkAndFetchData();
+    },
+    getUnitId(newUnitId) {
+      this.checkAndFetchData();
+    },
+  },
+  computed: {
+    ...mapGetters(["getBookId", "getUnitId"]),
+
+    processedRoadmap() {
+      let problemCounter = 0;
+
+      return this.roadmapData.map((item) => {
+          problemCounter++;
+          return {
+            label: `문제${problemCounter}`,
+            learning_id: item.prblm_id,
+            seqNum: item.seq,
+            prblm_type_id: item.prblm_type_id,
+          };
+        
+      });
+    },
+  },
+  beforeDestroy() {
+    // 컴포넌트가 파괴되기 전에 리스너 제거
+    window.removeEventListener("resize", this.updateCanvasRect);
+    this.$refs.canvas.removeEventListener("click", this.handleCanvasClick);
+  },
 };
 </script>
 
-<style>
+<style scoped>
+.complete-wrap img {
+  width: auto;
+}
 .body {
   width: 1435px;
   height: auto;
@@ -789,7 +1323,7 @@
 
 video {
   width: 100%;
-  height: 100%;
+  height: auto;
   background-color: #666;
 }
 
@@ -801,16 +1335,6 @@
   background-color: #fff3d7;
   cursor: not-allowed;
 }
-
-/* button {
-    margin: auto;
-    padding: 5px 10px;
-    font-size: 13px;
-    cursor: pointer;
-    display: flex;
-    justify-content: center;
-    text-align: center;
-} */
 
 .sticker {
   position: absolute;
@@ -858,6 +1382,28 @@
 
 .rabbit-end {
   cursor: pointer;
+  display: block;
+  position: absolute;
+  bottom: 0px;
+  left: -15px;
+  z-index: 10000;
+}
+
+.rabbit-running {
+  position: absolute;
+  /* bottom: 40px; */
+  /* right: 110px; */
+  top: -118px;
+  z-index: 10000;
+  transform: scaleX(-1);
+  transition: all 0.5s ease-in-out;
+}
+
+.fireworks-end {
+  position: absolute;
+  bottom: 70px;
+  left: -40px;
+  width: 20rem;
 }
 
 .camera-rabbit {
@@ -865,38 +1411,6 @@
   right: 0;
   bottom: 0;
   width: 40%;
-}
-
-.rabbit-start img {
-  transform: scaleX(-1);
-}
-
-.image-container {
-  position: relative;
-  display: inline-block;
-}
-
-.clear-img {
-  position: absolute;
-  top: -27px;
-  left: 0;
-  width: 100px;
-  height: 100px;
-  z-index: 1;
-}
-
-.race-btn p.before-clear {
-  /* width: auto; */
-  font-size: 20px;
-  line-height: 1.6;
-  color: #ffffff;
-  background-color: rgba(48, 48, 48, 0.562);
-  padding: 6px;
-  border-radius: 8px;
-  /* border: 1px solid rgba(255, 255, 255, 0.2); */
-  margin-bottom: 20px;
-  /* box-shadow: none; */
-  position: static;
 }
 
 .race-btn p.clear {
@@ -915,12 +1429,43 @@
   gap: 0.5rem;
 }
 
+.race-btn p.before-clear {
+  /* width: auto; */
+  font-size: 20px;
+  line-height: 1.6;
+  color: #ffffff;
+  background-color: rgba(119, 119, 119, 0.815);
+  padding: 6px;
+  border-radius: 8px;
+  /* border: 1px solid rgba(255, 255, 255, 0.2); */
+  margin-bottom: 20px;
+  /* box-shadow: none; */
+  position: static;
+}
+
 .race-box .lcon {
   display: flex;
   align-items: center;
 }
+.lcon .race-btn .rabbit-running {
+  transform: scaleX(1);
+}
 
-.complete-wrap img {
-  width: auto;
+.rabbit-start img {
+  transform: scaleX(-1);
+}
+
+.image-container {
+  position: relative;
+  display: inline-block;
+}
+
+.clear-img {
+  position: absolute;
+  top: -27px;
+  left: 0;
+  width: 100px;
+  height: 100px;
+  z-index: 1;
 }
 </style>
client/views/pages/main/Dashboard.vue
--- client/views/pages/main/Dashboard.vue
+++ client/views/pages/main/Dashboard.vue
@@ -1057,6 +1057,7 @@
       console.log("레이블된 아이템: ", labeledItems);
       if (labeledItems.label.startsWith("문제")) {
         this.handleProblemDetail(this.$store.getters.currentLearningId);
+        console.log(">>>>>>>>>>", this.$store.getters.currentLearningId)
         this.goToPage(this.problemType);
       } else if (labeledItems.label.startsWith("단어장")) {
         this.handleWordBookContent(this.$store.getters.getLearningId);
client/views/pages/main/MyPlan.vue
--- client/views/pages/main/MyPlan.vue
+++ client/views/pages/main/MyPlan.vue
@@ -76,7 +76,7 @@
                     <div style="justify-content: right; width: 100%; display: flex;"
                         v-for="(ai_learning, index) in aiLearningList" :key="index">
                         <div style="margin-right: 2em;"
-                            @click="goToPage2('AIDashboard', aiLearningList[index].unit_id)">
+                            @click="goToPage2('AIDashboard', aiLearningList[index].unit_id); recommendLearning();">
                             <img src="../../../resources/img/new_img/plan/ai_course_1.png">
                         </div>
                         <div style="margin-right: 2em;"
@@ -360,7 +360,7 @@
                     "Content-Type": "application/json; charset=UTF-8",
                 },
                 data: {
-                    std_id: this.stdId
+                    user_id: "USID_000000000000002"
                 }
             })
                 .then(function (response) {
@@ -373,6 +373,25 @@
                 });
         },
 
+        recommendLearning() {
+            axios({
+                url: "http://165.229.169.32:35716/recommend_and_insert",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    "top_n": 11,
+                    "user_id": "USID_000000000000002"
+                },
+            })
+            .then((response) => {
+                console.log("recommendLearning list - response : ", response.data);
+            })
+            .catch((err) => {
+                console.log("추천 에러: ", err);
+            });
+        },
     },
     watch: {},
     computed: {
Add a comment
List