SyuanYu 1 年之前
父節點
當前提交
8b8abb0e7d

+ 17 - 14
src/assets/css/style.css

@@ -70,6 +70,10 @@ h2 {
   font-weight: 500;
 }
 
+*:focus-visible {
+  outline: none;
+}
+
 input:focus-visible {
   outline: 2px solid var(--sub-color);
 }
@@ -374,7 +378,11 @@ input:focus-visible {
   -webkit-line-clamp: 2;
 }
 .main-card .card-info p {
-  -webkit-line-clamp: 3;
+  -webkit-line-clamp: 4;
+}
+.main-card .card-info p b,
+.main-card .card-info p strong {
+  font-weight: 400;
 }
 .main-card .card-info {
   height: 85%;
@@ -459,33 +467,28 @@ input:focus-visible {
   z-index: 1000;
 }
 
-@media (max-width: 960px) {
-  .tag-btn {
-    margin: auto !important;
-    max-width: 300px;
-    flex-direction: column;
-  }
-}
 .tag-btn .item {
   display: flex;
   align-items: center;
   justify-content: center;
+  width: 100%;
   color: var(--purple);
   border: 1px solid var(--purple);
-  border-radius: 20px;
+  border-radius: 10px;
   text-align: center;
   transition: all 0.3s;
+  line-height: 24px;
+  letter-spacing: 1px;
 }
 .tag-btn .item:hover {
   color: #fff;
   border-color: var(--purple);
   background-color: var(--purple);
 }
-.tag-btn .item a {
-  width: 100%;
-  display: block;
-  line-height: 24px;
-  letter-spacing: 1px;
+.tag-btn .item h3 {
+  padding: 10px;
+  font-weight: 500;
+  word-break: keep-all;
 }
 
 .hint {

File diff suppressed because it is too large
+ 0 - 0
src/assets/css/style.css.map


+ 31 - 12
src/assets/css/style.scss

@@ -73,6 +73,10 @@ h2 {
   font-weight: 500;
 }
 
+*:focus-visible {
+  outline: none;
+}
+
 input:focus-visible {
   outline: 2px solid (var(--sub-color));
 }
@@ -381,7 +385,12 @@ input:focus-visible {
   }
 
   .card-info p {
-    -webkit-line-clamp: 3;
+    -webkit-line-clamp: 4;
+
+    b,
+    strong {
+      font-weight: 400;
+    }
   }
 
   .card-info {
@@ -483,21 +492,25 @@ input:focus-visible {
 }
 
 .tag-btn {
-  @media (max-width: 960px) {
-    margin: auto !important;
-    max-width: 300px;
-    flex-direction: column;
-  }
+  // @media (max-width: 960px) {
+  //   margin: auto !important;
+  //   max-width: 300px;
+  //   flex-direction: column;
+  // }
 
   .item {
     display: flex;
     align-items: center;
     justify-content: center;
+    width: 100%;
+    // display: block;
     color: var(--purple);
     border: 1px solid var(--purple);
-    border-radius: 20px;
+    border-radius: 10px;
     text-align: center;
     transition: all .3s;
+    line-height: 24px;
+    letter-spacing: 1px;
 
     &:hover {
       color: #fff;
@@ -505,12 +518,18 @@ input:focus-visible {
       background-color: var(--purple);
     }
 
-    a {
-      width: 100%;
-      display: block;
-      line-height: 24px;
-      letter-spacing: 1px;
+    h3 {
+      padding: 10px;
+      font-weight: 500;
+      word-break: keep-all;
     }
+
+    // a {
+    //   width: 100%;
+    //   display: block;
+    //   line-height: 24px;
+    //   letter-spacing: 1px;
+    // }
   }
 }
 

二進制
src/assets/img/college-group/future/proposal/購買連結-1.png


二進制
src/assets/img/college-group/future/proposal/購買連結-2.png


二進制
src/assets/img/college-group/future/proposal/購買連結-3.png


二進制
src/assets/img/college-group/future/proposal/購買連結-4.png


二進制
src/assets/img/college-group/future/proposal/購買連結-5.png


+ 21 - 4
src/components/CourseCard.vue

@@ -128,9 +128,15 @@ function isClassFavorite(classId) {
       <!-- </router-link> -->
       <ul v-if="data.org !== 'Udemy'">
         <li class="d-flex align-center mb-5">
-          <p class="text-gray pt-3">
+          <!-- <p class="text-gray pt-3">
             {{ data.introduction }}
-          </p>
+          </p> -->
+          <div class="description">
+            <p
+              class="text-gray pt-3"
+              v-html="store.formatString(data.introduction)"
+            ></p>
+          </div>
         </li>
         <li class="d-flex align-center mt-auto">
           <v-icon color="primary" icon="mdi-map-marker" class="me-1"></v-icon>
@@ -160,11 +166,22 @@ function isClassFavorite(classId) {
 
       <ul v-else class="justify-center">
         <li>
-          <p class="text-gray pt-3">
+          <div class="description">
+            <p class="text-gray pt-3" v-html="store.formatString(data.content)"></p>
+          </div>
+
+          <!-- <p class="text-gray pt-3">
             {{ data.content }}
-          </p>
+          </p> -->
         </li>
       </ul>
     </div>
   </div>
 </template>
+
+<style lang="scss" scoped>
+.description {
+  height: 105px;
+  overflow: hidden;
+}
+</style>

+ 46 - 9
src/components/Navbar.vue

@@ -1,5 +1,5 @@
 <script setup>
-import { ref, reactive,onMounted } from "vue";
+import { ref, reactive, onMounted } from "vue";
 import { useRouter } from "vue-router";
 import { useMainStore } from "@/stores/store";
 import { useI18n } from "vue-i18n";
@@ -11,6 +11,7 @@ const { t, locale } = useI18n();
 let menuShow = ref(false);
 let collegeMenuShow = ref(false);
 let otherMenuShow = ref(false);
+let loginMenuShow = ref(false);
 
 function toggleMenu() {
   collegeMenuShow.value = false;
@@ -26,8 +27,10 @@ function handleMouseEvents(name, event) {
   if (screenWidth >= 1280) {
     if (name === "college") {
       collegeMenuShow.value = event;
-    } else {
+    } else if (name === "other") {
       otherMenuShow.value = event;
+    } else {
+      loginMenuShow.value = event;
     }
   } else {
     return;
@@ -91,14 +94,14 @@ const otherList = reactive([
   // },
 ]);
 
-onMounted(()=>{
-  console.log('onMounted');
+onMounted(() => {
+  console.log("onMounted");
   localStorage.setItem("lang", "zh");
-})
+});
 
 function changeLang() {
-  let lang =  localStorage.getItem("lang");
-  console.log('lang',lang);
+  let lang = localStorage.getItem("lang");
+  console.log("lang", lang);
   if (lang === "zh") {
     locale.value = "en";
     localStorage.setItem("lang", "en");
@@ -107,6 +110,11 @@ function changeLang() {
     localStorage.setItem("lang", "zh");
   }
 }
+
+function handleLogout() {
+  localStorage.removeItem("token");
+  window.location.reload();
+}
 </script>
 
 <template>
@@ -163,7 +171,7 @@ function changeLang() {
       <!-- <li>
         <router-link :to="'/article'">知識文章</router-link>
       </li> -->
-      <li>
+      <li class="position-relative">
         <v-dialog
           v-model="store.loginDialog"
           max-width="450"
@@ -174,7 +182,36 @@ function changeLang() {
           </template>
           <Login @close="handleClose" />
         </v-dialog>
-        <router-link :to="'/user/profile'" v-else>會員專區</router-link>
+
+        <div v-else>
+          <router-link
+            :to="'/user/profile'"
+            @mouseover="loginMenuShow = true"
+            @mouseleave="loginMenuShow = false"
+            class="d-none d-lg-block"
+            >會員專區</router-link
+          >
+
+          <router-link :to="'/user/profile'" class="d-block d-lg-none"
+            >會員專區</router-link
+          >
+        </div>
+
+        <div
+          v-if="store.loginState"
+          class="college-slider"
+          :class="{ slider: loginMenuShow }"
+          @mouseover="handleMouseEvents('login', true)"
+          @mouseleave="handleMouseEvents('login', false)"
+        >
+          <ul>
+            <li>
+              <a href="javascript:;" @click="handleLogout()"> 登出 </a>
+            </li>
+          </ul>
+        </div>
+
+        <!-- <router-link :to="'/user/profile'" v-else>會員專區</router-link> -->
       </li>
       <li>
         <router-link :to="'/setup-courses'">我要開課</router-link>

+ 1 - 0
src/components/PDFViewer.vue

@@ -20,6 +20,7 @@ watch(
 );
 
 function setPDFUrl(pdf) {
+  console.log('pdf',pdf);
   // let url = `https://ntcri.org/pdf/${name}.pdf`;
   let json = pdf.replace(/'/g, '"');
   let file = JSON.parse(json);

+ 16 - 1
src/router/index.js

@@ -22,6 +22,7 @@ const CollegeGroup = defineAsyncComponent(() => import('@/views/CollegeGroup/Mai
 const Online = defineAsyncComponent(() => import('@/views/CollegeGroup/Online.vue'));
 const Craft = defineAsyncComponent(() => import('@/views/CollegeGroup/Craft.vue'));
 const Future = defineAsyncComponent(() => import('@/views/CollegeGroup/Future.vue'));
+const Proposal = defineAsyncComponent(() => import('@/views/CollegeGroup/Proposal.vue'));
 const Cross = defineAsyncComponent(() => import('@/views/CollegeGroup/Cross.vue'));
 const Life = defineAsyncComponent(() => import('@/views/CollegeGroup/Life.vue'));
 const Cfa = defineAsyncComponent(() => import('@/views/CollegeGroup/Cfa.vue'));
@@ -257,6 +258,11 @@ const routes = [
       },
     ],
   },
+  {
+    path: '/college-group/future/proposal/:id',
+    name: 'Proposal',
+    component: Proposal
+  },
   {
     path: '/news',
     name: 'News',
@@ -349,8 +355,17 @@ router.beforeEach((to, from, next) => {
   // 檢查目標路由是否需要驗證權限
   if (to.meta.requiresAuth) {
     const haveToken = store.loginState;
+    console.log('store token', store.token);
+    let isTokenExpired = store.checkTokenExpiration(); // 檢查 Token 是否過期
     if (haveToken) {
-      next(); // 允許使用者訪問目標頁面
+      if (!isTokenExpired) {
+        next(); // 允許使用者訪問目標頁面
+      } else {
+        store.loginState = false;
+        localStorage.removeItem("token");
+        next('/'); // 若 token 過期將重定向至首頁
+      }
+      // next();
     } else {
       store.loginDialog = true; // 開啟登入視窗
       next('/'); // 若無 token 直接輸入網址前往時,將重定向至首頁

+ 48 - 0
src/stores/store.js

@@ -76,6 +76,54 @@ export const useMainStore = defineStore('mainStore', {
     // 開啟登入視窗
     openLoginDialog() {
       this.loginDialog = true;
+    },
+    // 調整字串
+    formatString(content) {
+      if (content) {
+        content = content.replace(/&lt;/g, "<");
+        content = content.replace(/&gt;/g, ">");
+        content = content.replace(/&amp;nbsp;/g, " ");
+        content = content.replace(/&amp;times;/g, "×");
+        content = content.replace(/&amp;#39;/g, "'");
+        content = content.replace(/&middot;/g, "·");
+
+        return content;
+      }
+    },
+    checkTokenExpiration() {
+      if (this.token) {
+        // 解碼 JWT
+        const base64Url = this.token.split(".")[1]; // 取得 JWT 的 payload
+        const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/"); // 處理 URL 安全的 Base64 字串
+        const decodedPayload = atob(base64); // Base64 解碼
+
+        // 將解碼後的 payload 轉換為 JSON 物件
+        const payload = JSON.parse(decodedPayload);
+
+        // 獲取過期時間(exp)
+        const expirationTime = payload.exp; // 過期時間的 UNIX 時間戳
+
+        // 創建 Date 物件並設定時間戳
+        const expirationDate = new Date(expirationTime * 1000); // JS 時間戳以毫秒為單位故乘以 1000
+
+        // 取得日期和時間的字串表示
+        const formattedExpiration = expirationDate.toLocaleString();
+
+        console.log("JWT 過期時間:", formattedExpiration);
+
+        // 取得當前時間戳
+        const currentTimestamp = Math.floor(Date.now() / 1000);
+
+        let isTokenExpired;
+
+        // 判斷 Token 是否已過期
+        if (currentTimestamp > expirationTime) {
+          isTokenExpired = true;
+        } else {
+          isTokenExpired = false;
+        }
+        return isTokenExpired;
+      }
     }
   },
 })

+ 37 - 10
src/views/CollegeGroup/Future.vue

@@ -1,8 +1,7 @@
 <script setup>
 import { ref, reactive } from "vue";
 import { useMainStore } from "@/stores/store";
-import moment from "moment";
-import readList from "@/utils/useReadList";
+import axios from "axios";
 
 const store = useMainStore();
 
@@ -22,7 +21,22 @@ const breadcrumbs = reactive([
   },
 ]);
 
-const bookList = readList.slice().splice(2, 4);
+let read = reactive({
+  list: [],
+});
+
+// 線上閱讀
+(async () => {
+  try {
+    const response = await axios.get(
+      "https://cmm.ai:8088/api/get_article?category=研究計畫"
+    );
+    read.list = response.data.articles;
+    console.log("線上閱讀", read.list);
+  } catch (error) {
+    console.error(error);
+  }
+})();
 </script>
 
 <template>
@@ -90,7 +104,7 @@ const bookList = readList.slice().splice(2, 4);
 
   <v-container class="book-block">
     <v-row
-      v-for="(item, index) in bookList"
+      v-for="(item, index) in read.list"
       :key="index"
       class="align-center mb-16"
     >
@@ -101,20 +115,23 @@ const bookList = readList.slice().splice(2, 4);
           target="_blank"
           class="cover-img"
         >
-          <img :src="item.img" alt="" />
+          <img :src="`https://ntcri.org/${item.cover_img}`" alt="" />
           <button class="read-btn">點我閱讀</button>
         </a>
 
-        <router-link v-else :to="`/crafts/${item.fileName}`" class="cover-img">
-          <img :src="item.img" alt="" />
+        <!-- <router-link v-else :to="`/crafts/${item.fileName}`" class="cover-img"> -->
+        <router-link
+          v-else
+          :to="`/college-group/future/proposal/${item.article_id}`"
+          class="cover-img"
+        >
+          <img :src="`https://ntcri.org/${item.cover_img}`" alt="" />
           <button class="read-btn">點我閱讀</button>
         </router-link>
         <h3 v-html="item.title"></h3>
-        <h3>{{ item.depiction }}</h3>
       </v-col>
       <v-col cols="12" md="8" class="px-md-16 pb-md-16 mb-md-10">
-        <h4 v-html="item.caption"></h4>
-        <p class="mt-10">{{ item.content }}</p>
+        <p class="mt-10 content" v-html="item.depiction"></p>
       </v-col>
     </v-row>
   </v-container>
@@ -241,5 +258,15 @@ const bookList = readList.slice().splice(2, 4);
       }
     }
   }
+
+  .content {
+    // 超過兩行則省略
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-line-clamp: 5;
+    -webkit-box-orient: vertical;
+    line-break: after-white-space;
+  }
 }
 </style>

+ 4 - 0
src/views/CollegeGroup/Main.vue

@@ -32,6 +32,10 @@ getGroup(groupId.value);
 async function getGroup(id) {
   console.log("getGroup", id);
 
+  if (!id) {
+    return;
+  }
+
   if (id === 1) {
     groupName = "未來工藝";
   } else if (id === 2) {

+ 265 - 0
src/views/CollegeGroup/Proposal.vue

@@ -0,0 +1,265 @@
+<script setup>
+import { ref, reactive } from "vue";
+import { useRoute, useRouter } from "vue-router";
+import { useMainStore } from "@/stores/store";
+import axios from "axios";
+import moment from "moment";
+import Navbar from "@/components/Navbar.vue";
+
+const store = useMainStore();
+const route = useRoute();
+const router = useRouter();
+const courseId = route.params.id; // 網址參數
+
+console.log("courseId", courseId);
+
+const breadcrumbs = reactive([
+  {
+    title: "首頁",
+    disabled: false,
+    href: "/",
+  },
+  {
+    title: "未來工藝",
+    disabled: false,
+    href: "/college-group/future",
+  },
+  {
+    title: "研究計畫",
+    disabled: true,
+  },
+]);
+
+let articles = reactive({
+  list: [],
+});
+
+let articlesAll = reactive({
+  list: [],
+});
+
+let buyWay = reactive({
+  list: [],
+});
+
+// 線上閱讀
+(async () => {
+  console.log("courseId", courseId);
+  try {
+    const response = await axios.get(
+      `https://cmm.ai:8088/api/get_article?category=研究計畫&article_id=${courseId}`
+    );
+    articlesAll.list = response.data.articles;
+    articles.list = response.data.articles[0];
+    if (articles.list.buy_way !== "undefined") {
+      buyWay.list = JSON.parse(articles.list.buy_way);
+    }
+    console.log("線上閱讀", articles);
+    console.log("buyWay.list", buyWay.list);
+  } catch (error) {
+    console.error(error);
+  }
+})();
+
+function handlerBuyImg(title) {
+  console.log("title", title);
+  let img;
+
+  if (title === "博客來") {
+    img = store.getImageUrl("college-group/future/proposal/購買連結-1.png");
+  } else if (title === "金石堂") {
+    img = store.getImageUrl("college-group/future/proposal/購買連結-2.png");
+  } else if (title === "誠品線上") {
+    img = store.getImageUrl("college-group/future/proposal/購買連結-3.png");
+  } else if (title === "國家書店") {
+    img = store.getImageUrl("college-group/future/proposal/購買連結-4.png");
+  }
+
+  return img;
+}
+
+function setPDFUrl(pdf) {
+  if (pdf) {
+    let json = pdf.replace(/'/g, '"');
+    let file = JSON.parse(json);
+    let url = file.file1;
+    console.log("setPDFUrl url", url);
+
+    return url;
+  }
+}
+</script>
+
+<template>
+  <Navbar />
+  <v-container>
+    <v-breadcrumbs
+      :items="breadcrumbs"
+      divider="/"
+      class="my-10 p-0"
+    ></v-breadcrumbs>
+
+    <v-row class="my-16 proposal-container">
+      <v-col cols="12" sm="8" md="9">
+        <section>
+          <h2 class="proposal-title" v-html="articles.list.title"></h2>
+
+          <div class="content">
+            <p v-html="articles.list.content"></p>
+
+            <div v-if="articles.list.files !== '{}'" class="read-btn">
+              <!-- <router-link :to="`/crafts/${articles.list.id}`"
+                >點擊閱讀書籍</router-link
+              > -->
+              <a :href="setPDFUrl(articles.list.files)" target="_blank"
+                >點擊閱讀書籍</a
+              >
+            </div>
+          </div>
+        </section>
+      </v-col>
+      <v-col cols="12" sm="4" md="3" class="d-flex flex-column align-center">
+        <img
+          class="cover-img"
+          src="@/assets/img/college-group/future/素材-10.png"
+          alt=""
+        />
+        <span class="caption my-5">本書資訊</span>
+        <ul>
+          <li>著作:{{ articles.list.writer }}</li>
+          <li>出版社:{{ articles.list.publish }}</li>
+          <li>
+            出版日期:{{
+              moment(`${articles.list.publish_date}`).format("YYYY/MM/DD")
+            }}
+          </li>
+          <li>語言:{{ articles.list.language }}</li>
+          <li class="store-list">
+            <p>購買:</p>
+            <ul>
+              <li v-for="(item, index) in buyWay.list" :key="index">
+                <a :href="item.url" target="_blank">
+                  <img :src="handlerBuyImg(item.title)" alt="" />
+                </a>
+                <!-- <a href="">
+                  <img
+                    src="@/assets/img/college-group/future/proposal/購買連結-2.png"
+                    alt=""
+                  />
+                </a> -->
+              </li>
+            </ul>
+          </li>
+        </ul>
+      </v-col>
+    </v-row>
+  </v-container>
+</template>
+
+<style lang="scss">
+.proposal-container {
+  @media (max-width: 600px) {
+    flex-direction: column-reverse;
+  }
+
+  .proposal-title {
+    padding: 40px 10px;
+    margin-bottom: 40px;
+    text-align: center;
+    font-size: 24px;
+    letter-spacing: 2px;
+    line-height: 36px;
+    border-left: 15px solid var(--blue);
+    @media (max-width: 600px) {
+      font-size: 20px;
+    }
+  }
+
+  ul {
+    width: 100%;
+    line-height: 24px;
+    li {
+      margin-bottom: 5px;
+      line-height: 28px;
+      letter-spacing: 1px;
+      a {
+        display: block;
+        margin-bottom: -10px;
+      }
+    }
+  }
+
+  .cover-img {
+    width: 100%;
+    height: 350px;
+    object-fit: contain;
+  }
+
+  .caption {
+    display: block;
+    width: 100%;
+    padding: 12px 30px;
+    color: #fff;
+    letter-spacing: 1px;
+    background-color: var(--blue);
+    border-radius: 10px;
+    text-shadow: 1px 1px 3px #939393;
+  }
+
+  .store-list {
+    display: flex;
+    p {
+      width: 65px;
+      white-space: nowrap;
+    }
+    img {
+      width: 200px;
+    }
+  }
+
+  .content {
+    padding: 50px;
+    border: 1px solid var(--blue);
+    border-radius: 10px;
+
+    h2 {
+      font-size: 20px;
+    }
+
+    p {
+      letter-spacing: 1px;
+    }
+
+    img {
+      padding: 10px 15px;
+      @media (max-width: 600px) {
+        padding: 0 0 15px;
+      }
+    }
+
+    .read-btn {
+      display: flex;
+      justify-content: end;
+      margin-top: 50px;
+
+      a {
+        padding: 15px 40px;
+        font-size: 18px;
+        letter-spacing: 2px;
+        text-shadow: 1px 1px 3px #939393;
+        background-color: #a7c2cd;
+        color: white;
+        cursor: pointer;
+        border-radius: 100px;
+        transition: all 0.3s;
+        &:hover {
+          background-color: #99b1bb;
+        }
+        @media (max-width: 600px) {
+          font-size: 16px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 5 - 1
src/views/CourseDetail.vue

@@ -306,7 +306,7 @@ let currentTitle = computed(() => {
               </tr>
               <tr>
                 <td>課程簡介</td>
-                <td>{{ course.data.introduction }}</td>
+                <td v-html="store.formatString(course.data.introduction)"></td>
               </tr>
               <tr v-show="course.data.category !== ''">
                 <td>課程類別</td>
@@ -783,6 +783,10 @@ let currentTitle = computed(() => {
         &:first-child {
           border-right: 1px solid #333;
         }
+
+        strong {
+          font-weight: 500;
+        }
       }
       th {
         padding-bottom: 20px;

+ 1 - 1
src/views/CourseList.vue

@@ -58,7 +58,7 @@ async function getClass() {
     courseData.classes = response.data.classes;
     totalNum.value = response.data.total_num;
     loading.value = false;
-    console.log("response", response);
+    console.log("getClass response", response);
   } catch (error) {
     loading.value = false;
     console.error(error);

+ 164 - 17
src/views/Courses/Create.vue

@@ -128,6 +128,12 @@ let location = reactive({
 let locationId = ref("");
 
 async function insertSchool() {
+  // 若已選擇據點則不需新增
+  if (schoolId.value !== "") {
+    locationId.value = schoolId.value;
+    return;
+  }
+
   const formData = new FormData();
   for (const key in location) {
     formData.append(key, location[key]);
@@ -158,9 +164,9 @@ let resume = reactive({
   introduction: "", // 老師介紹
 });
 
-async function insertUserResume() {
-  console.log("insertUserResume", resume);
+let oldFile = "";
 
+async function insertUserResume() {
   if (portfolioImg.value.length) {
     resume.files = portfolioImg.value;
   }
@@ -180,7 +186,7 @@ async function insertUserResume() {
 
   try {
     const response = await axios.post(
-      `https://cmm.ai:8088/api/input_user_resume?access_token=${token}`,
+      `https://cmm.ai:8088/api/input_user_resume?access_token=${token}&old_file=${oldFile}`,
       formData
     );
     console.log("新增履歷 response", response);
@@ -415,7 +421,7 @@ const handlePortfolioImg = (files) => {
     console.log("file", file);
     let url = URL.createObjectURL(file);
     portfolioImgList.value.push(url);
-    console.log("portfolioImgList", portfolioImgList);
+    console.log("portfolioImgList", portfolioImgList.value);
   }
 };
 
@@ -475,6 +481,7 @@ let sessionEndTimeList = ref(["", "", "", "", "", "", ""]);
 
 // 新增課堂
 async function insertSession() {
+  console.log("isOneDay.value", isOneDay.value);
   const weekString = `[${selectedWeek.value.join(",")}]`;
   session.week_day_str = weekString;
 
@@ -504,34 +511,153 @@ async function insertSession() {
       sessionStartTimeList.value[index] = '""';
       sessionEndTimeList.value[index] = '""';
     }
-
-    console.log(
-      "sessionStartTimeList.value[index]",
-      sessionStartTimeList.value[index]
-    );
   });
 
   session.start_week_time = `[${sessionStartTimeList.value}]`;
   session.end_week_time = `[${sessionEndTimeList.value}]`;
   session.class_event_id = eventId.value;
 
-  console.log("session >>", session);
+  // const formData = new FormData();
+  // for (const key in session) {
+  //   formData.append(key, session[key]);
+  // }
 
-  const formData = new FormData();
-  for (const key in session) {
-    formData.append(key, session[key]);
+  let oneDayData = {
+    class_event_id: session.class_event_id,
+    start_time: event.start_time,
+    end_time: event.end_time,
+    content: "",
+  };
+
+  console.log("oneDayData", oneDayData);
+
+  // const oneDayFormData = new FormData();
+  // for (const key in oneDayData) {
+  //   formData.append(key, oneDayData[key]);
+  // }
+
+  try {
+    if (isOneDay.value) {
+      const oneDayFormData = new FormData();
+      for (const key in oneDayData) {
+        oneDayFormData.append(key, oneDayData[key]);
+      }
+
+      // 一日課程
+      const response = await axios.post(
+        "https://cmm.ai:8088/api/insert_session",
+        oneDayFormData
+      );
+      console.log("新增課堂(一日) response", response);
+    } else {
+      const formData = new FormData();
+      for (const key in session) {
+        formData.append(key, session[key]);
+      }
+
+      // 週期課程
+      const response = await axios.post(
+        "https://cmm.ai:8088/api/auto_create_session",
+        formData
+      );
+      console.log("新增課堂(週期) response", response);
+    }
+    // const response = await axios.post(
+    //   "https://cmm.ai:8088/api/auto_create_session",
+    //   formData
+    // );
+    // console.log("新增課堂 response", response);
+  } catch (error) {
+    console.error(error);
   }
+}
+
+let schoolSelect = reactive({
+  list: [],
+});
 
+let schoolId = ref("");
+
+// 取得已有據點
+(async () => {
+  let token = store.token;
   try {
-    const response = await axios.post(
-      "https://cmm.ai:8088/api/auto_create_session",
-      formData
+    const response = await axios.get(
+      `https://cmm.ai:8088/api/get_school?access_token=${token}`
     );
-    console.log("新增課堂 response", response);
+
+    schoolSelect.list = response.data.schools;
+    console.log("取得使用者據點", schoolSelect.list);
+  } catch (error) {
+    console.error(error);
+  }
+})();
+
+// 監聽據點 Select
+watch(schoolId, (id) => {
+  handleSchoolData(id);
+});
+
+let schools = reactive({
+  list: [],
+});
+
+// 帶入據點資料
+async function handleSchoolData(id) {
+  console.log("handleSchoolData", id);
+  try {
+    const response = await axios.get(
+      `https://cmm.ai:8088/api/get_school?location_id=${id}`
+    );
+
+    schools.list = response.data.schools;
+    console.log("取得已有據點", schools.list);
+
+    for (const key in schools.list[0]) {
+      if (location.hasOwnProperty(key) && schools.list[0][key] !== undefined) {
+        location[key] = schools.list[0][key];
+      }
+    }
   } catch (error) {
     console.error(error);
   }
 }
+
+// 取得已有履歷
+(async () => {
+  let token = store.token;
+  try {
+    const response = await axios.get(
+      `https://cmm.ai:8088/api/get_user_resume?access_token=${token}`
+    );
+
+    let userResume = response.data.user_resume;
+    console.log("已有履歷", userResume);
+    for (const key in userResume) {
+      if (resume.hasOwnProperty(key) && userResume[key] !== undefined) {
+        resume[key] = userResume[key];
+      }
+      if (key === "imgs") {
+        // 存入已有圖片
+        oldFile = userResume[key];
+
+        // 顯示已上傳圖片
+        let json = JSON.parse(userResume[key].replace(/'/g, '"'));
+        json.map((item) => {
+          console.log(
+            "`https://ntcri.org/${item}`",
+            `https://ntcri.org/${item}`
+          );
+          portfolioImgList.value.push(`https://ntcri.org/${item}`);
+        });
+      }
+    }
+
+    console.log("取得已有履歷", userResume);
+  } catch (error) {
+    console.error(error);
+  }
+})();
 </script>
 
 <template>
@@ -553,6 +679,22 @@ async function insertSession() {
       <v-window v-model="step">
         <v-window-item :value="1">
           <v-card-text>
+            <div v-if="schoolSelect.list.length" class="mb-10 school-list">
+              <v-label class="d-flex align-center w-100 overflow-visible">
+                <p class="pe-3">已經有據點?</p>
+                <v-select
+                  v-model="schoolId"
+                  label="請選擇據點"
+                  :items="schoolSelect.list"
+                  item-title="location_name"
+                  item-value="location_id"
+                  hide-details
+                  density="compact"
+                  variant="outlined"
+                ></v-select>
+              </v-label>
+            </div>
+
             <v-label class="d-flex align-center pb-3">
               <p class="pb-5 pe-3">據點名稱<span class="mark">*</span></p>
               <v-text-field
@@ -1648,4 +1790,9 @@ async function insertSession() {
     white-space: nowrap;
   }
 }
+
+.school-list {
+  width: 350px;
+  margin: auto;
+}
 </style>

+ 27 - 22
src/views/Crafts.vue

@@ -5,7 +5,6 @@ import { useMainStore } from "@/stores/store";
 import axios from "axios";
 import Navbar from "@/components/Navbar.vue";
 import PDFViewer from "@/components/PDFViewer.vue";
-import readList from "@/utils/useReadList";
 import craftsPdfList from "@/utils/useCraftsPdf";
 import ArticleCard from "@/components/ArticleCard.vue";
 import CraftsArticle from "@/components/CraftsArticle.vue";
@@ -27,7 +26,10 @@ if (routeHash) {
 
 onMounted(() => {
   if (bookName) {
-    updatePDF(bookName);
+    setTimeout(() => {
+      let book = read.list.filter((item) => item.article_id == bookName);
+      updatePDF(book[0].files);
+    }, 1000);
   }
 });
 
@@ -143,10 +145,6 @@ const tagList = reactive([
   },
 ]);
 
-// function getPDF(name) {
-//   return `https://ntcri.org/pdf/${name}.pdf`;
-// }
-
 let fileName = ref(""); // PDFViewer Props
 const readRef = ref(null);
 const viewerRef = ref(null);
@@ -163,7 +161,6 @@ function updatePDF(name) {
 }
 
 function isAnchorLink(url) {
-  console.log("url", url, url.startsWith("#"));
   // 檢查 URL 是否為錨點
   return url.startsWith("#");
 }
@@ -231,19 +228,20 @@ function handlePdfUrl(pdf) {
           <v-col
             v-for="(item, index) in tagList"
             :key="index"
-            class="ma-2 item"
+            :cols="index + 1 === tagList.length ? '12' : '6'"
+            md=""
           >
             <a
               v-if="isAnchorLink(item.url)"
               :href="item.url"
-              class="py-3 py-lg-6"
+              class="py-3 py-lg-6 item"
             >
               <!-- 錨點 -->
-              {{ item.title }}
+              <h3>{{ item.title }}</h3>
             </a>
             <a v-else :href="item.url" target="_blank" class="py-3 py-lg-6">
               <!-- 網址 -->
-              {{ item.title }}
+              <h3>{{ item.title }}</h3>
             </a>
           </v-col>
         </v-row>
@@ -275,14 +273,14 @@ function handlePdfUrl(pdf) {
 
         <h2 ref="readRef" id="readList">線上閱讀</h2>
 
-        <v-row class="justify-center mt-16 read-list">
+        <v-row class="justify-center align-start mt-16 read-list">
           <v-col
             cols="12"
             sm="6"
             md="4"
             v-for="(item, index) in read.list"
             :key="index"
-            class="d-flex flex-column align-center px-10"
+            class="d-flex flex-row flex-sm-column align-center px-sm-10"
           >
             <a
               v-if="store.isMobile"
@@ -292,7 +290,6 @@ function handlePdfUrl(pdf) {
               <v-img
                 class="mx-auto cover-img"
                 :lazy-src="`https://ntcri.org/${item.cover_img}`"
-                cover
                 :src="`https://ntcri.org/${item.cover_img}`"
               >
                 <template v-slot:placeholder>
@@ -318,7 +315,6 @@ function handlePdfUrl(pdf) {
               v-else
               class="mx-auto cover-img"
               :lazy-src="`https://ntcri.org/${item.cover_img}`"
-              cover
               :src="`https://ntcri.org/${item.cover_img}`"
               @click="updatePDF(item.files)"
             >
@@ -337,7 +333,7 @@ function handlePdfUrl(pdf) {
             </a> -->
             <section>
               <h3 v-html="item.title"></h3>
-              <p>{{ item.depiction }}</p>
+              <!-- <p>{{ item.depiction }}</p> -->
             </section>
           </v-col>
         </v-row>
@@ -532,21 +528,30 @@ h2 {
 .read-list {
   .v-img {
     width: 100%;
-    height: 450px;
+    // height: 100%;
+    max-height: 400px;
     object-fit: cover;
     cursor: pointer;
 
-    @media (max-width: 1280px) {
-      height: 300px;
+    @media (max-width: 600px) {
+      width: 50%;
     }
-    @media (max-width: 960px) {
-      height: auto;
-      max-width: 300px;
+  }
+
+  @media (max-width: 600px) {
+    section {
+      width: 50%;
+      padding: 0 10px;
+      font-size: 14px;
     }
   }
 
   p {
     width: 290px;
+
+    @media (max-width: 600px) {
+      width: auto;
+    }
   }
 }
 

+ 20 - 13
src/views/Home.vue

@@ -255,9 +255,9 @@ function selectTag(btn) {
           當期展覽
         </v-btn>
       </div>
-     <div class="d-flex justify-end">
-      <router-link to="/news">觀看更多訊息 >></router-link>
-     </div>
+      <div class="d-flex justify-end">
+        <router-link to="/news">觀看更多訊息 >></router-link>
+      </div>
       <ul>
         <li v-for="(item, index) in news.list" :key="index" class="mb-16 list">
           <HomeList :data="item" />
@@ -291,7 +291,7 @@ function selectTag(btn) {
 
     <h2 class="my-10 title">{{ t("home.title_2") }}</h2>
     <v-row>
-      <v-col cols="12" sm="6" md="4">
+      <v-col cols="6" md="4">
         <router-link to="/" class="img-info">
           <img src="@/assets/img/home/首頁元素-12.png" alt="" />
           <section>
@@ -299,7 +299,7 @@ function selectTag(btn) {
           </section>
         </router-link>
       </v-col>
-      <v-col cols="12" sm="6" md="4">
+      <v-col cols="6" md="4">
         <router-link to="/college-group/craft" class="img-info">
           <img src="@/assets/img/home/首頁元素-11.png" alt="" />
           <section>
@@ -307,7 +307,7 @@ function selectTag(btn) {
           </section>
         </router-link>
       </v-col>
-      <v-col cols="12" sm="6" md="4">
+      <v-col cols="6" md="4">
         <router-link to="/college-group/future" class="img-info">
           <img src="@/assets/img/home/首頁元素-06.png" alt="" />
           <section>
@@ -315,7 +315,7 @@ function selectTag(btn) {
           </section>
         </router-link>
       </v-col>
-      <v-col cols="12" sm="6" md="4">
+      <v-col cols="6" md="4">
         <router-link to="/college-group/cross" class="img-info">
           <img src="@/assets/img/home/首頁元素-09.png" alt="" />
           <section>
@@ -323,7 +323,7 @@ function selectTag(btn) {
           </section>
         </router-link>
       </v-col>
-      <v-col cols="12" sm="6" md="4">
+      <v-col cols="6" md="4">
         <router-link to="/college-group/craft-for-all" class="img-info">
           <img src="@/assets/img/home/臺灣綠工藝希望工程.png" alt="" />
           <section>
@@ -331,7 +331,7 @@ function selectTag(btn) {
           </section>
         </router-link>
       </v-col>
-      <v-col cols="12" sm="6" md="4">
+      <v-col cols="6" md="4">
         <router-link to="/college-group/life" class="img-info">
           <img src="@/assets/img/home/首頁元素-07.png" alt="" />
           <section>
@@ -546,9 +546,14 @@ function selectTag(btn) {
 
     <h2 class="mb-10 mt-16 title">{{ t("crafts.title") }}</h2>
 
-    <v-row class="px-10 mt-16 mb-10 tag-btn">
-      <v-col v-for="(item, index) in tagList" :key="index" class="ma-2 item">
-        <a :href="item.url" target="_blank" class="py-3">
+    <v-row class="px-5 px-sm-10 mt-16 mb-10 tag-btn">
+      <v-col
+        v-for="(item, index) in tagList"
+        :key="index"
+        :cols="index + 1 === tagList.length ? '12' : '6'"
+        md=""
+      >
+        <a :href="item.url" target="_blank" class="py-3 py-lg-6 item">
           <!-- 網址 -->
           {{ t(`${item.title}`) }}
         </a>
@@ -811,8 +816,10 @@ function selectTag(btn) {
 }
 
 .v-expansion-panel-title {
-  font-size: 16px;
   padding: 20px 30px;
+  font-size: 16px;
+  line-height: 26px;
+  letter-spacing: 1px;
   border: 1px solid var(--blue);
 }
 

+ 2 - 23
src/views/Login.vue

@@ -107,6 +107,7 @@ async function login() {
     setTimeout(() => {
       localStorage.setItem("token", response.data.access_token);
       store.loginState = true;
+      window.location.reload();
     }, 1500);
   } catch (error) {
     console.error(error);
@@ -201,28 +202,6 @@ onMounted(() => {
   window.onSignIn1 = handleSignIn;
 });
 
-let token =
-  "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIiLCJleHAiOjE2ODg2MjU5MzN9.rMJGZxHeKaxBQx0VuTBk8AtYhhLyIPxNUHBrsKWXJjE";
-
-// 解碼 JWT
-const base64Url = token.split(".")[1]; // 取得 JWT 的 payload
-const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/"); // 處理 URL 安全的 Base64 字串
-const decodedPayload = atob(base64); // Base64 解碼
-
-// 將解碼後的 payload 轉換為 JSON 物件
-const payload = JSON.parse(decodedPayload);
-
-// 獲取過期時間(exp)
-const expirationTime = payload.exp; // 過期時間的 UNIX 時間戳
-
-// 創建 Date 物件並設定時間戳
-const expirationDate = new Date(expirationTime * 1000); // JS 時間戳以毫秒為單位故乘以 1000
-
-// 取得日期和時間的字串表示
-const formattedExpiration = expirationDate.toLocaleString();
-
-console.log("JWT 過期時間:", formattedExpiration);
-
 let showLogin = ref(false);
 
 function toggleLoginInput() {
@@ -312,7 +291,7 @@ function toggleLoginInput() {
               >
             </v-form>
           </Transition>
-          
+
           <div class="alert" v-if="loginAlert">
             <v-alert border="start" border-color="primary" elevation="2">
               {{ alertText }}

+ 254 - 116
src/views/User/Courses.vue

@@ -370,7 +370,17 @@ async function getEvent(id) {
 }
 
 let enrolledLoading = ref(false);
-let registrations = reactive({
+
+// 審核狀態
+let registrationState = ref(false);
+
+// 待審核報名清單
+let pendingRegistration = reactive({
+  list: [],
+});
+
+// 已審核報名清單
+let approvedRegistration = reactive({
   list: [],
 });
 
@@ -378,15 +388,37 @@ let registrations = reactive({
 async function getEnrolledData(eventId) {
   enrolledLoading.value = true;
   console.log("getEnrolledData eventId", eventId);
+
+  const formData = new FormData();
+  formData.append("class_id", eventId);
+
   try {
     const response = await axios.get(
       `https://cmm.ai:8088/api/get_registration_class?event_id=${eventId}&access_token=${token}`
     );
+    const recordResponse = await axios.post(
+      `https://cmm.ai:8088/api/add_attend_record_by_event`,
+      formData
+    );
     console.log("取得報名", response);
-    registrations.list = response.data.registrations;
-    console.log("報名清單", registrations.list);
+    console.log("新增課堂報名資料", recordResponse);
+
+    pendingRegistration.list = response.data.registrations.filter(
+      (item) => item.reg_confirm === 0
+    );
+
+    if (pendingRegistration.list.length) {
+      registrationState.value = true;
+    } else {
+      registrationState.value = false;
+    }
+
+    approvedRegistration.list = response.data.registrations.filter(
+      (item) => item.reg_confirm === 1
+    );
+    console.log("已審核", approvedRegistration.list);
 
-    registrations.list.map((item) => {
+    approvedRegistration.list.map((item) => {
       if (item.payment_status) {
         item.payment_status = true;
       } else {
@@ -419,7 +451,7 @@ async function updateEnrolledData(item, index) {
   // 若取消付款狀態則清空末五碼欄位
   if (!data.payment_status) {
     data.five_digits = "00000";
-    registrations.list[index].five_digits = "";
+    approvedRegistration.list[index].five_digits = "";
   }
 
   console.log("data >", data);
@@ -484,41 +516,8 @@ let date = reactive({
   registration_end_time: "", // 報名截止時間
 });
 
-let itemsPerPage = ref(10); // 每頁顯示筆數(查看出缺席)
-let headers = reactive([
-  {
-    title: "姓名",
-    key: "real_name",
-  },
-  { title: "電話", key: "phone" },
-  { title: "信箱", key: "email" },
-  { title: "付款狀態", key: "payment" },
-  { title: "末五碼", key: "five_digits" },
-]);
-
-let studentHeaders = reactive([
-  {
-    title: "姓名",
-    align: "start",
-    key: "real_name",
-  },
-  { title: "電話", key: "phone" },
-  { title: "信箱", key: "email" },
-  { title: "出席", key: "is_attend" },
-]);
-
-let sessionHeaders = reactive([
-  {
-    title: "課堂編號",
-    align: "center",
-    key: "sessions",
-  },
-  { title: "開始時間", align: "center", key: "start_time" },
-  { title: "結束時間", align: "center", key: "end_time" },
-  { title: "學員名單", align: "center", key: "student" },
-]);
-
 let studentDialog = ref(false);
+
 let record = reactive({
   list: [],
 });
@@ -550,6 +549,7 @@ async function getAttendRecord(id) {
 // 更新課堂出席名單
 async function saveAttendRecord(item) {
   const data = {
+    id: item.raw.id,
     class_detail_id: item.raw.class_detail_id,
     user_id: item.raw.user_id,
     is_attend: item.raw.is_attend ? 1 : 0,
@@ -562,17 +562,92 @@ async function saveAttendRecord(item) {
 
   console.log("data", data);
 
+  try {
+    const response = await axios.get(
+      `https://cmm.ai:8088/api/update_attend_record?id=${data.id}&class_detail_id=${data.class_detail_id}&user_id=${data.user_id}&is_attend=${data.is_attend}`
+    );
+
+    console.log("更新報名清單", response);
+  } catch (error) {
+    console.error(error);
+  }
+}
+
+let tab = ref(2);
+
+async function updateRegConfirm(item) {
+  console.log("updateRegConfirm", item);
+
+  const data = {
+    is_attend: item.is_attend,
+    payment_status: 0,
+    reg_confirm: 1, // 審核確認
+    five_digits: "00000",
+  };
+
+  const formData = new FormData();
+  for (const key in data) {
+    formData.append(key, data[key]);
+  }
+
   try {
     const response = await axios.post(
-      "https://cmm.ai:8088/api/add_attend_record",
+      `https://cmm.ai:8088/api/update_registration_class?event_id=${item.event_id}&user_id=${item.user_id}&access_token=${token}`,
       formData
     );
 
-    console.log("更新報名清單", response);
+    console.log("更新審核", response);
+
+    getEnrolledData(item.event_id);
   } catch (error) {
     console.error(error);
   }
 }
+
+let itemsPerPage = ref(10); // 每頁顯示筆數(查看出缺席)
+
+let pendingHeaders = reactive([
+  {
+    title: "姓名",
+    key: "real_name",
+  },
+  { title: "電話", key: "phone" },
+  { title: "信箱", key: "email" },
+  { title: "審核", key: "reg_confirm" },
+]);
+
+let approvedHeaders = reactive([
+  {
+    title: "姓名",
+    key: "real_name",
+  },
+  { title: "電話", key: "phone" },
+  { title: "信箱", key: "email" },
+  { title: "付款狀態", key: "payment" },
+  { title: "末五碼", key: "five_digits" },
+]);
+
+let studentHeaders = reactive([
+  {
+    title: "姓名",
+    align: "start",
+    key: "real_name",
+  },
+  { title: "電話", key: "phone" },
+  { title: "信箱", key: "email" },
+  { title: "出席", key: "is_attend" },
+]);
+
+let sessionHeaders = reactive([
+  {
+    title: "課堂編號",
+    align: "center",
+    key: "sessions",
+  },
+  { title: "開始時間", align: "center", key: "start_time" },
+  { title: "結束時間", align: "center", key: "end_time" },
+  { title: "學員名單", align: "center", key: "student" },
+]);
 </script>
 
 <template>
@@ -672,27 +747,62 @@ async function saveAttendRecord(item) {
                       ></v-progress-circular>
                     </div>
 
-                    <v-data-table
-                      v-else
-                      v-model:items-per-page="itemsPerPage"
-                      :headers="headers"
-                      :items="registrations.list"
-                      item-value="name"
-                      class="courses-table"
-                    >
-                      <!-- <template v-slot:item.enrolled="{ item }">
+                    <div v-else>
+                      <v-tabs
+                        v-model="tab"
+                        color="purple"
+                        align-tabs="center"
+                        class="mb-5 courses-tab"
+                      >
+                        <v-tab :value="1" class="me-5">
+                          未審核
+                          <span v-show="registrationState" class="badge"></span
+                        ></v-tab>
+                        <v-tab :value="2">已審核</v-tab>
+                      </v-tabs>
+                      <v-window v-model="tab">
+                        <v-window-item :value="1">
+                          <v-data-table
+                            v-model:items-per-page="itemsPerPage"
+                            :headers="pendingHeaders"
+                            :items="pendingRegistration.list"
+                            item-value="name"
+                            class="courses-table"
+                          >
+                            <template v-slot:item.reg_confirm="{ item }">
+                              <v-btn
+                                class="my-3 me-4"
+                                color="purple"
+                                icon="mdi-check"
+                                @click="updateRegConfirm(item.raw)"
+                              >
+                              </v-btn>
+                            </template>
+                          </v-data-table>
+                        </v-window-item>
+                        <v-window-item :value="2">
+                          <v-data-table
+                            v-model:items-per-page="itemsPerPage"
+                            :headers="approvedHeaders"
+                            :items="approvedRegistration.list"
+                            item-value="name"
+                            class="courses-table"
+                          >
+                            <!-- <template v-slot:item.enrolled="{ item }">
                         <v-checkbox hide-details></v-checkbox>
                       </template> -->
-                      <template v-slot:item.payment="{ item }">
-                        <div class="d-flex align-center my-2">
-                          <v-checkbox
-                            v-model="item.selectable.payment_status"
-                            color="purple"
-                            hide-details
-                            label="已付款"
-                            @change="updateEnrolledData(item.raw, item.index)"
-                          ></v-checkbox>
-                          <!-- <v-text-field
+                            <template v-slot:item.payment="{ item }">
+                              <div class="d-flex align-center my-2">
+                                <v-checkbox
+                                  v-model="item.selectable.payment_status"
+                                  color="purple"
+                                  hide-details
+                                  label="已付款"
+                                  @change="
+                                    updateEnrolledData(item.raw, item.index)
+                                  "
+                                ></v-checkbox>
+                                <!-- <v-text-field
                             v-model="item.selectable.five_digits"
                             density="compact"
                             variant="outlined"
@@ -741,69 +851,78 @@ async function saveAttendRecord(item) {
                               </v-chip>
                             </Transition>
                           </div> -->
-                        </div>
-                      </template>
-                      <template v-slot:item.five_digits="{ item }">
-                        <div class="d-flex align-center my-2">
-                          <!-- <v-checkbox
+                              </div>
+                            </template>
+                            <template v-slot:item.five_digits="{ item }">
+                              <div class="d-flex align-center my-2">
+                                <!-- <v-checkbox
                             v-model="item.selectable.payment_status"
                             color="purple"
                             hide-details
                             label="已付款"
                             @change="updateEnrolledData(item.raw, item.index)"
                           ></v-checkbox> -->
-                          <v-text-field
-                            v-model="item.selectable.five_digits"
-                            density="compact"
-                            variant="outlined"
-                            hide-details
-                            label="帳號末五碼"
-                            class="account-item"
-                            :disabled="!item.selectable.payment_status"
-                            @input="fiveDigitsInput[item.index] = true"
-                          ></v-text-field>
+                                <v-text-field
+                                  v-model="item.selectable.five_digits"
+                                  density="compact"
+                                  variant="outlined"
+                                  hide-details
+                                  label="帳號末五碼"
+                                  class="account-item"
+                                  :disabled="!item.selectable.payment_status"
+                                  @input="fiveDigitsInput[item.index] = true"
+                                ></v-text-field>
 
-                          <div style="width: 95px">
-                            <div v-if="fiveDigitsInput[item.index]">
-                              <v-btn
-                                @click="
-                                  updateEnrolledData(item.raw, item.index)
-                                "
-                                variant="outlined"
-                                rounded
-                                color="success"
-                                size="xs-small"
-                                class="mx-3"
-                              >
-                                <v-icon icon="mdi-check" class="pa-4"></v-icon>
-                              </v-btn>
-                              <v-btn
-                                @click="
-                                  fiveDigitsInput[item.index] = false;
-                                  item.selectable.five_digits = '';
-                                "
-                                variant="outlined"
-                                rounded
-                                color="gray"
-                                size="xs-small"
-                              >
-                                <v-icon icon="mdi-close" class="pa-4"></v-icon>
-                              </v-btn>
-                            </div>
-                            <Transition>
-                              <v-chip
-                                v-if="showChip[item.index]"
-                                class="ms-3"
-                                color="success"
-                                variant="outlined"
-                              >
-                                儲存成功
-                              </v-chip>
-                            </Transition>
-                          </div>
-                        </div>
-                      </template>
-                    </v-data-table>
+                                <div style="width: 95px">
+                                  <div v-if="fiveDigitsInput[item.index]">
+                                    <v-btn
+                                      @click="
+                                        updateEnrolledData(item.raw, item.index)
+                                      "
+                                      variant="outlined"
+                                      rounded
+                                      color="success"
+                                      size="xs-small"
+                                      class="mx-3"
+                                    >
+                                      <v-icon
+                                        icon="mdi-check"
+                                        class="pa-4"
+                                      ></v-icon>
+                                    </v-btn>
+                                    <v-btn
+                                      @click="
+                                        fiveDigitsInput[item.index] = false;
+                                        item.selectable.five_digits = '';
+                                      "
+                                      variant="outlined"
+                                      rounded
+                                      color="gray"
+                                      size="xs-small"
+                                    >
+                                      <v-icon
+                                        icon="mdi-close"
+                                        class="pa-4"
+                                      ></v-icon>
+                                    </v-btn>
+                                  </div>
+                                  <Transition>
+                                    <v-chip
+                                      v-if="showChip[item.index]"
+                                      class="ms-3"
+                                      color="success"
+                                      variant="outlined"
+                                    >
+                                      儲存成功
+                                    </v-chip>
+                                  </Transition>
+                                </div>
+                              </div>
+                            </template>
+                          </v-data-table>
+                        </v-window-item>
+                      </v-window>
+                    </div>
 
                     <!-- <v-data-table
                       v-else
@@ -1621,13 +1740,16 @@ async function saveAttendRecord(item) {
   .v-data-table-footer {
     padding: 20px 0;
   }
+
   .v-selection-control {
     justify-content: center;
   }
+
   .account-item {
     .v-field__input {
       padding: 0 20px;
     }
+
     .v-label {
       color: var(--gray);
     }
@@ -1637,4 +1759,20 @@ async function saveAttendRecord(item) {
 .v-data-table-header__content {
   justify-content: center;
 }
+
+.courses-tab {
+  .v-tab {
+    font-size: 18px;
+  }
+  .badge {
+    display: block;
+    height: 10px;
+    width: 10px;
+    background-color: red;
+    border-radius: 100px;
+    position: absolute;
+    right: 0px;
+    top: 10px;
+  }
+}
 </style>

Some files were not shown because too many files changed in this diff