SyuanYu 1 年之前
父節點
當前提交
ad835006f6

二進制
src/assets/img/home/logo-center.png


二進制
src/assets/img/home/logo.png


二進制
src/assets/img/logo.png


二進制
src/assets/img/passport/materials.jpg


+ 45 - 12
src/components/CourseCard.vue

@@ -101,6 +101,25 @@ async function deleteFavoriteClass(classId) {
 //     return `${store.imgUrl}/${data.cover_img}`;
 //   }
 // }
+
+// 傳遞參數到父組件 (CourseList.vue)
+const emits = defineEmits(["set-category"]);
+const handleCategory = (category) => {
+  emits("set-category", category);
+};
+
+function returnLinkUrl(data) {
+  if (data.direct_url && data.direct_url !== "") {
+    return data.direct_url;
+  } else {
+    if (data.org !== "Udemy") {
+      return `/course-detail/${data.class_name_id}`;
+    } else {
+      return data.video_url;
+    }
+  }
+  // data.org !== 'Udemy' ? $router.resolve(`/course-detail/${data.class_name_id}`).href : data.video_url
+}
 </script>
 
 <template>
@@ -110,13 +129,7 @@ async function deleteFavoriteClass(classId) {
     </section>
     <div class="card-info">
       <!-- <router-link :to="`/course-detail/${data.class_name_id}`"> -->
-      <a
-        :href="
-          data.org !== 'Udemy'
-            ? $router.resolve(`/course-detail/${data.class_name_id}`).href
-            : data.video_url
-        "
-      >
+      <a :href="returnLinkUrl(data)">
         <div class="overflow-hidden">
           <v-img
             class="mx-auto cover-img"
@@ -138,16 +151,22 @@ async function deleteFavoriteClass(classId) {
         </div>
       </a>
 
-      <div
-        v-if="data.encode"
-        class="d-flex align-center justify-space-between mt-3"
-      >
+      <div class="d-flex align-center justify-space-between mt-5">
+        <v-chip
+          @click="handleCategory(data.category)"
+          v-if="data.category"
+          label
+          color="primary"
+          class="category-item"
+        >
+          {{ data.category }}
+        </v-chip>
+
         <div>
           <v-chip v-if="data.level" size="small">
             {{ data.level }}
           </v-chip>
         </div>
-        <small class="text-gray">編號:{{ data.encode }}</small>
       </div>
       <!-- </router-link> -->
 
@@ -205,6 +224,10 @@ async function deleteFavoriteClass(classId) {
           </p> -->
         </li>
       </ul>
+
+      <div v-if="data.encode" class="d-flex align-center justify-end my-2">
+        <small class="text-gray">編號:{{ data.encode }}</small>
+      </div>
     </div>
   </div>
 </template>
@@ -218,4 +241,14 @@ async function deleteFavoriteClass(classId) {
 .location-item {
   -webkit-line-clamp: 2 !important;
 }
+
+.card-info {
+  .category-item {
+    cursor: pointer;
+    transition: all 0.3s;
+    &:hover {
+      opacity: 0.85;
+    }
+  }
+}
 </style>

+ 16 - 18
src/components/Navbar.vue

@@ -388,34 +388,32 @@ function handleLogout() {
 
 <style lang="scss" scoped>
 .navbar {
-  width: 90%;
-  max-width: 81.25em;
-  margin: 0 auto 2.5em;
+  max-width: 90em;
+  margin: 40px auto 0;
   padding: 0 2.1875em 0 0.9375em;
+  position: sticky;
+  top: 0;
+  z-index: 2000;
+  background: #fff;
   border: 0.0625em solid;
-  position: relative;
-  z-index: 1000;
-  background: transparent;
-  top: 2.5em;
-  left: 0;
-  right: 0;
 
   @media (max-width: 1280px) {
+    margin: 40px 40px 0;
+    padding: 0.625em 1.25em 0.625em 0.625em;
+  }
+
+  @media (max-width: 600px) {
+    margin: 20px 20px 0;
     padding: 0.625em 1.25em 0.625em 0.625em;
   }
 
   img {
     width: 100%;
-    max-width: 370px;
+    max-width: 300px;
     margin-top: 0.3125em;
-    @media (max-width: 1400px) {
-      max-width: 300px;
-    }
-    @media (max-width: 1280px) {
-      max-width: 400px;
-    }
+
     @media (max-width: 600px) {
-      max-width: 300px;
+      max-width: 230px;
     }
   }
 
@@ -426,7 +424,7 @@ function handleLogout() {
 
     @media (max-width: 1280px) {
       position: absolute;
-      top: 5.3em;
+      top: 6.2em;
       left: 0;
       right: 0;
       z-index: 100;

+ 1 - 0
src/language/en.json

@@ -273,6 +273,7 @@
     "auto_fill_location_name": "Auto-fill Location Name",
     "cover_image": "Cover Image",
     "upload_course_cover_image": "Upload Course Cover Image",
+    "upload_portfolio_image": "Upload Portfolio Image",
     "choose_image": "Choose Image",
     "provide_name": "Provide your name or your workshop, brand, or institute name",
     "safe_accessible_location": "Courses must be held at a safe and accessible location",

+ 2 - 1
src/language/zh.json

@@ -272,6 +272,7 @@
     "auto_fill_location_name": "自動帶入據點名稱",
     "cover_image": "課程封面",
     "upload_course_cover_image": "上傳課程封面",
+    "upload_portfolio_image": "上傳工藝成品圖",
     "choose_image": "選擇相片上傳",
     "provide_name": "可以是您的工作室或品牌名稱/教學單位/您的姓名",
     "safe_accessible_location": "需在安全且便於學徒到達之地點開課",
@@ -299,7 +300,7 @@
   "all": "全部",
   "awaiting_approval": "未審核",
   "approved": "已審核",
-  "rejected": "已駁回",
+  "rejected": "待修正補充",
   "closed": "已關閉",
   "proposal_awaiting_approval": "提案審核中",
   "send": "送出",

+ 2 - 2
src/views/ArticleDetail.vue

@@ -82,7 +82,7 @@ getArticle();
                 </p>
               </div>
               <h2 class="text-center">{{ list.data.title }}</h2>
-              <!-- <v-img
+              <v-img
                 class="cover-img my-10"
                 :lazy-src="`${store.imgUrl}/${list.data.cover_img}`"
                 :src="`${store.imgUrl}/${list.data.cover_img}`"
@@ -96,7 +96,7 @@ getArticle();
                     ></v-progress-circular>
                   </div>
                 </template>
-              </v-img> -->
+              </v-img>
             </v-col>
             <v-col cols="12">
               <section

+ 11 - 7
src/views/CollegeGroup/Cross.vue

@@ -302,14 +302,18 @@ async function handleSearch() {
 
   <div class="craftplus-item mb-6"></div>
 
-  <v-row class="pb-10 craftplus-block">
+  <iframe
+    src="https://craftplus.ntcri.gov.tw/"
+    width="100%"
+    height="600"
+    frameborder="0"
+  ></iframe>
+
+  <!-- <div class="craftplus-item mb-6"></div> -->
+
+  <!-- <v-row class="pb-10 craftplus-block">
     <v-col cols="2">
       <ul class="category-list">
-        <!-- <li v-for="(item, index) in categoryList" :key="index" class="mx-3">
-          <router-link :to="item.path">
-            {{ item.title }}
-          </router-link>
-        </li> -->
         <li class="mb-8">
           <img src="@/assets/img/craftplus-logo.png" alt="" />
         </li>
@@ -448,7 +452,7 @@ async function handleSearch() {
         </v-col>
       </v-row>
     </v-col>
-  </v-row>
+  </v-row> -->
 
   <!-- <div class="craftplus-item mt-16"></div> -->
 

+ 113 - 67
src/views/CollegeGroup/Online.vue

@@ -29,28 +29,28 @@ const breadcrumbs = reactive([
   },
 ]);
 
-const categoryList = reactive([
-  {
-    id: 1256,
-    title: "匠作之手",
-    active: true,
-  },
-  {
-    id: 1255,
-    title: "手作學習",
-    active: false,
-  },
-  {
-    id: 1221,
-    title: "工藝行旅",
-    active: false,
-  },
-  {
-    id: 1253,
-    title: "檢測修復",
-    active: false,
-  },
-]);
+// const categoryList = reactive([
+//   {
+//     id: 1256,
+//     title: "匠作之手",
+//     active: true,
+//   },
+//   {
+//     id: 1255,
+//     title: "手作學習",
+//     active: false,
+//   },
+//   {
+//     id: 1221,
+//     title: "工藝行旅",
+//     active: false,
+//   },
+//   {
+//     id: 1253,
+//     title: "檢測修復",
+//     active: false,
+//   },
+// ]);
 
 let loading = ref(false);
 let pageNum = ref(1); // 頁數(預設第一頁)
@@ -124,15 +124,15 @@ async function selectFilter(type, val) {
 
 let activeCategory = ref(1256);
 
-function setCategory(id, index) {
+function setCategory(id) {
   activeCategory.value = id;
   // 移除 active
-  categoryList.map((item) => {
-    if (item.active) {
-      item.active = false;
-    }
-  });
-  categoryList[index].active = true;
+  // categoryList.map((item) => {
+  //   if (item.active) {
+  //     item.active = false;
+  //   }
+  // });
+  // categoryList[index].active = true;
   console.log("activeCategory.value", activeCategory.value);
   getImediaVideo();
 }
@@ -151,11 +151,16 @@ watch(imediaCurrentPage, () => {
 });
 
 let videoLoading = ref(false);
+let categoryList = ref([]);
 
 async function getImediaVideo() {
   videoLoading.value = true;
 
-  let url = `${store.apiUrl}/api/get_media_data?media_categories_id=${activeCategory.value}&page=${imediaCurrentPage.value}&page_size=8`;
+  let url = `${store.apiUrl}/api/get_media_data?page=${imediaCurrentPage.value}&page_size=8`;
+
+  if (activeCategory.value) {
+    url += `&media_categories_id=${activeCategory.value}`;
+  }
 
   try {
     imedia.list = [];
@@ -167,6 +172,19 @@ async function getImediaVideo() {
       }
     });
     console.log("Imedia data", response.data);
+    console.log("list", list);
+    console.log("list category", list.category[0]);
+    categoryList.value = [];
+    list.category[0].categories.map((item) => {
+      // console.log('item',item);
+      // media_category_id
+      let data = {
+        id: item.media_category_id,
+        title: item.name[1].zh_TW,
+      };
+      categoryList.value.push(data);
+    });
+    console.log("categoryList.value", categoryList.value);
 
     imediaTotalPages.value = store.getTotalPages(imedia.list.length, 8); // 計算頁數
     console.log("imediaTotalPages", imediaTotalPages.value);
@@ -180,6 +198,13 @@ async function getImediaVideo() {
 }
 
 getImediaVideo();
+
+let videoCategory = ref(null);
+
+watch(videoCategory, (val) => {
+  console.log("videoCategory", val);
+  setCategory(val);
+});
 </script>
 
 <template>
@@ -219,8 +244,24 @@ getImediaVideo();
   </div>
 
   <v-row>
-    <v-col sm="2" cols="12" class="px-0">
-      <ul class="btn-list">
+    <v-col md="2" cols="12">
+      <v-label class="d-block filter-list">
+        <p class="pb-5 pe-3 mb-5 mb-sm-0">
+          {{ t("form.craft_category") }}<span class="mark">*</span>
+        </p>
+        <v-select
+          v-model="videoCategory"
+          placeholder="請選擇類別"
+          :items="categoryList"
+          item-title="title"
+          item-value="id"
+          :rules="[requiredRule]"
+          density="compact"
+          variant="outlined"
+        ></v-select>
+      </v-label>
+
+      <!-- <ul class="btn-list">
         <li v-for="(item, index) in categoryList" :key="index" class="mx-3">
           <v-btn
             @click="setCategory(item.id, index)"
@@ -231,9 +272,9 @@ getImediaVideo();
             {{ item.title }}
           </v-btn>
         </li>
-      </ul>
+      </ul> -->
     </v-col>
-    <v-col sm="10" cols="12">
+    <v-col md="10" cols="12">
       <div class="d-flex justify-center mb-10" v-if="videoLoading">
         <v-progress-circular
           color="grey-lighten-4"
@@ -439,7 +480,7 @@ getImediaVideo();
   >
 </template>
 
-<style lang="scss" scoped>
+<style lang="scss">
 .v-input {
   @media (max-width: 600px) {
     max-width: 15.625em;
@@ -448,39 +489,10 @@ getImediaVideo();
   }
 }
 
-.btn-list {
-  display: grid;
-  grid-template-columns: repeat(auto-fit, minmax(6.875em, max-content));
-  justify-content: center;
-  padding: initial;
-
-  li {
-    list-style-type: none;
-  }
-
-  .v-btn {
-    width: 6.25em;
-    color: var(--purple);
-    border-color: var(--purple);
-    @media (max-width: 600px) {
-      width: 9.375em;
-    }
-
-    &:hover {
-      color: #fff;
-      background-color: var(--purple);
-    }
-
-    &:hover > .v-btn__overlay {
-      opacity: 0;
-    }
-  }
-
-  .active {
-    color: #fff;
-    background-color: var(--purple);
-  }
+.filter-list .v-select .v-field__input {
+  padding-top: 0.55em !important;
 }
+
 .video-list {
   h3 {
     margin: 0.5em 0 0.3em;
@@ -574,4 +586,38 @@ video {
     }
   }
 }
+
+// .btn-list {
+//   display: grid;
+//   grid-template-columns: repeat(auto-fit, minmax(6.875em, max-content));
+//   justify-content: center;
+//   padding: initial;
+
+//   li {
+//     list-style-type: none;
+//   }
+
+//   .v-btn {
+//     width: 6.25em;
+//     color: var(--purple);
+//     border-color: var(--purple);
+//     @media (max-width: 600px) {
+//       width: 9.375em;
+//     }
+
+//     &:hover {
+//       color: #fff;
+//       background-color: var(--purple);
+//     }
+
+//     &:hover > .v-btn__overlay {
+//       opacity: 0;
+//     }
+//   }
+
+//   .active {
+//     color: #fff;
+//     background-color: var(--purple);
+//   }
+// }
 </style>

+ 2 - 1
src/views/CourseDetail.vue

@@ -431,6 +431,7 @@ function isDateExpired(dateString) {
                   v-if="
                     isInner !== 0 &&
                     session.data.length &&
+                    session.data[0]?.fee_payment &&
                     session.data[0]?.fee_payment !== null
                   "
                 >
@@ -975,7 +976,7 @@ function isDateExpired(dateString) {
         white-space: nowrap;
       }
       td {
-        padding: 1em;
+        padding: 1em 2em;
         text-align: center;
         vertical-align: middle;
         white-space: nowrap;

+ 120 - 27
src/views/CourseList.vue

@@ -15,7 +15,6 @@ let totalPages = ref(1); // 總頁數
 
 let loading = ref(false);
 let searchInput = ref("");
-let searchError = ref(false);
 
 const courseAll = reactive({
   classes: [],
@@ -26,6 +25,7 @@ const courseData = reactive({
 
 const listLocation = ref(null);
 let isInternal = ref(false); // 是否選取學習時數課程(只顯示內課)
+let isOvertime = ref(false); // 課程是否過期(過期 is_overtime = 1)
 let isFilter = ref(false); // 篩選狀態
 let searchList = reactive([]); // 篩選條件
 let totalNum = ref(0); // 資料總筆數
@@ -47,6 +47,11 @@ async function getClass() {
     url += "&is_inner=1";
   }
 
+  // 篩選過期課程
+  if (isOvertime.value) {
+    url += "&is_overtime=1";
+  }
+
   // 判斷篩選
   if (isFilter.value) {
     console.log("searchList", searchList);
@@ -93,13 +98,8 @@ async function search() {
     totalPages.value = store.getTotalPages(response.data.total_num, 18);
     courseData.classes = response.data.classes;
     loading.value = false;
+    courseCategory.value = "";
     console.log("搜尋 response", response);
-
-    if (!response.data.total_num) {
-      searchError.value = true;
-    } else {
-      searchError.value = false;
-    }
   } catch (error) {
     loading.value = false;
     console.error(error);
@@ -123,24 +123,29 @@ let assignTag = ref("all");
 watch(assignTag, (val) => {
   if (val === "all") {
     isInternal.value = false;
-  } else {
+    isOvertime.value = false;
+  } else if (val === "hours") {
     isInternal.value = true;
+    isOvertime.value = false;
+  } else if (val === "over") {
+    isInternal.value = false;
+    isOvertime.value = true;
   }
   pageNum.value = 1;
   getClass();
 });
 
 function selectTag(btn) {
-  if (btn === "all") {
-    assignTag.value = "all";
-  } else {
-    assignTag.value = "hours";
-  }
+  assignTag.value = btn;
+  // if (btn === "all") {
+  //   assignTag.value = "all";
+  // } else if (btn === "hours") {
+  //   assignTag.value = "hours";
+  // } else if (btn === "over") {
+  //   assignTag.value = "over";
+  // }
 }
 
-let selectSort = ref();
-let selectLocation = ref();
-let selectYear = ref();
 let selectCategory = ref([]);
 
 // 篩選課程
@@ -197,9 +202,12 @@ watch(selectedCounty, (newValue) => {
 // 清除重填(取得全部課程)
 function searchClear() {
   selectCategory.value = [];
+  selectCollege.value = null;
   selectedCounty.value = null;
   selectedDistrict.value = null;
   isFilter.value = false;
+  courseCategory.value = "";
+  locationKeyword.value = "";
   pageNum.value = 1;
   getClass();
 }
@@ -208,12 +216,24 @@ async function searchClass() {
   isFilter.value = true;
   pageNum.value = 1; // 篩選時返回第一頁
   searchList.splice(0, searchList.length); // 清空陣列
-
+  console.log("selectCategory", selectCategory.value);
   let categoryVal = selectCategory.value;
   if (categoryVal.length) {
+    if (courseCategory.value !== "") {
+      courseCategory.value = "";
+    }
     searchList.push(`&category=${categoryVal}`);
   }
 
+  if (courseCategory.value !== "" && !categoryVal.length) {
+    searchList.push(`&category=${courseCategory.value}`);
+  }
+
+  console.log("selectCollege.value", selectCollege.value);
+  if (selectCollege.value) {
+    searchList.push(`&group_id=${selectCollege.value}`);
+  }
+
   if (selectedCounty.value && selectedDistrict.value) {
     locationKeyword.value = `${selectedCounty.value}${selectedDistrict.value}`;
   } else if (selectedCounty.value && !selectedDistrict.value) {
@@ -226,6 +246,44 @@ async function searchClass() {
 onMounted(() => {
   store.getFavoriteClass();
 });
+
+const collegeList = reactive([
+  {
+    id: 1,
+    title: t("college_group_1"),
+  },
+  {
+    id: 2,
+    title: t("college_group_2"),
+  },
+  {
+    id: 7,
+    title: t("college_group_3"),
+  },
+  {
+    id: 8,
+    title: t("college_group_4"),
+  },
+  {
+    id: 9,
+    title: t("college_group_5"),
+  },
+  {
+    id: 3,
+    title: t("college_group_6"),
+  },
+]);
+
+let selectCollege = ref(null);
+
+const courseCategory = ref("");
+
+const setCategory = (newCategory) => {
+  courseCategory.value = newCategory;
+  selectCategory.value = [];
+  searchClass();
+  console.log("courseCategory", courseCategory.value);
+};
 </script>
 
 <template>
@@ -289,6 +347,17 @@ onMounted(() => {
             ></v-select>
           </v-col>
 
+          <v-col cols="12" sm="6" md="12" class="pb-0">
+            <v-select
+              v-model="selectCollege"
+              label="學群"
+              :items="collegeList"
+              item-title="title"
+              item-value="id"
+              hide-details
+            ></v-select>
+          </v-col>
+
           <v-col cols="12">
             <v-btn
               @click="searchClass()"
@@ -318,7 +387,7 @@ onMounted(() => {
       <v-col cols="12" md="9" lg="10">
         <div>
           <div
-            class="d-flex flex-column flex-md-row align-center justify-space-between mb-14"
+            class="d-flex flex-column flex-md-row align-center justify-space-between mb-14 position-relative"
             ref="listLocation"
           >
             <div class="d-flex tab-btn mb-8 mb-md-0">
@@ -329,6 +398,7 @@ onMounted(() => {
               >
                 {{ t("all_courses") }}
               </v-btn>
+
               <v-btn
                 variant="text"
                 @click="selectTag('hours')"
@@ -336,7 +406,14 @@ onMounted(() => {
               >
                 {{ t("learning_hours_courses") }}
               </v-btn>
-              <!-- <h2 class="me-5">課程清單</h2> -->
+
+              <v-btn
+                variant="text"
+                @click="selectTag('over')"
+                :class="{ active: assignTag === 'over' }"
+              >
+                過往課程
+              </v-btn>
             </div>
             <div class="search">
               <span>
@@ -354,6 +431,25 @@ onMounted(() => {
                 </button>
               </span>
             </div>
+
+            <div class="position-absolute" style="top: 42px; left: 10px">
+              <v-chip
+                v-if="courseCategory !== ''"
+                class="ms-2 my-5"
+                color="primary"
+                label
+              >
+                {{ courseCategory }}
+                <button>
+                  <v-icon
+                    @click="searchClear()"
+                    color="primary"
+                    icon="mdi-close-circle"
+                    class="ms-2"
+                  ></v-icon>
+                </button>
+              </v-chip>
+            </div>
           </div>
 
           <div class="d-flex justify-center mb-10" v-if="loading">
@@ -372,20 +468,20 @@ onMounted(() => {
               :key="index"
               class="pa-5"
             >
-              <CourseCard :data="item" />
+              <CourseCard :data="item" @set-category="setCategory" />
             </v-col>
           </v-row>
 
           <div
-            v-if="searchError"
-            class="d-flex justify-center align-center pt-12 me-4"
+            v-if="!totalNum"
+            class="d-flex justify-center align-center pt-12 me-4 mb-10"
           >
             <v-icon color="primary" icon="mdi-alert" class="me-2"></v-icon>
             {{ t("no_found") }}
           </div>
 
           <v-pagination
-            v-if="!searchError"
+            v-if="totalNum"
             v-model="pageNum"
             :length="totalPages"
             rounded="circle"
@@ -398,10 +494,7 @@ onMounted(() => {
         </div>
       </v-col>
       <v-col cols="12" class="my-16">
-        <img
-          src="@/assets/img/course/banner.webp"
-          alt="臺灣工藝學習平台"
-        />
+        <img src="@/assets/img/course/banner.webp" alt="臺灣工藝學習平台" />
       </v-col>
     </v-row>
   </v-container>

+ 169 - 15
src/views/Courses/Create.vue

@@ -22,18 +22,22 @@ let loading = ref(false);
 const computedTitle = computed(() => {
   switch (step.value) {
     case 1:
+      stepTitle.value = "個人資料蒐集同意書";
+      stepDescription.value = "";
+      break;
+    case 2:
       stepTitle.value = "select_location";
       stepDescription.value = "before_opening_course";
       break;
-    case 2:
+    case 3:
       stepTitle.value = "select_course_instructor";
       stepDescription.value = "craft_resume_intro";
       break;
-    case 3:
+    case 4:
       stepTitle.value = "create_course";
       stepDescription.value = "course_creation_bridge";
       break;
-    case 4:
+    case 5:
       stepTitle.value = "congratulations_course_creation";
       stepDescription.value = "";
       break;
@@ -799,6 +803,11 @@ function checkField(num) {
   console.log("checkField", num);
   // step.value++;
   if (num === 1) {
+    if (!consent_1.value || !consent_2.value) {
+      alert("尚未同意個人資料之提供");
+      return;
+    }
+  } else if (num === 2) {
     console.log("assignLocationId", assignLocationId.value);
 
     if (!assignLocationId.value) {
@@ -813,7 +822,7 @@ function checkField(num) {
     } else {
       getSchool(assignLocationId.value); // 取得指定據點
     }
-  } else if (num === 2) {
+  } else if (num === 3) {
     if (!assignTeachers.list.length) {
       errorField.value = true;
 
@@ -843,8 +852,13 @@ async function getSchool(id) {
     );
 
     console.log("取得據點", response);
-    resumeList.value = JSON.parse(response.data.schools[0].teachers_list);
-    console.log("resumeList.value", resumeList.value);
+    let teachersList = response.data.schools[0].teachers_list;
+    if (teachersList) {
+      resumeList.value = JSON.parse(teachersList);
+      console.log("resumeList.value", resumeList.value);
+    }
+    // resumeList.value = JSON.parse(teachersList);
+    // console.log("resumeList.value", resumeList.value);
     resumeLoading.value = false;
   } catch (error) {
     console.error(error);
@@ -893,6 +907,10 @@ function deleteEvent(index) {
   eventData.list.splice(index, 1);
   resumeDelectDialog[index] = false;
 }
+
+// 個人資料之同意提供
+let consent_1 = ref(false);
+let consent_2 = ref(false);
 </script>
 
 <template>
@@ -913,6 +931,122 @@ function deleteEvent(index) {
 
       <v-window v-model="step" :touch="false">
         <v-window-item :value="1">
+          <v-card-text>
+            <v-form
+              ref="consentForm"
+              class="mx-auto consent-form"
+              lazy-validation
+              @submit.prevent
+            >
+              <p>
+                臺灣工藝學習平台網站(以下稱「本平台」),在以各項業務之申請及資料維護等特定目的下進行個人資訊蒐集前,依個人資料保護法(以下簡稱個資法)第八條規定,告知下列事項,請您詳閱。
+              </p>
+
+              <ul>
+                <li>
+                  <h5>壹、蒐集個人資料之目的</h5>
+                  <p>
+                    本平台為執行業務管理與服務推廣相關業務所需,蒐集您的個
+                    人資料。
+                  </p>
+                </li>
+
+                <li>
+                  <h5>貳、蒐集個人資料之類別</h5>
+                  <ul>
+                    <li>
+                      一、本平台因執行業務蒐集您的個人資料包含:姓名、工作室名稱及地址、電話、電子信箱、匯款帳號、作品集、教學履歷。
+                    </li>
+                    <li>
+                      二、本平台將使用cookies
+                      進行各項網路資源服務之管理及記錄,包括蒐集IP位址、瀏覽網頁、使用檔案及時間等軌跡資料。
+                    </li>
+                  </ul>
+                </li>
+
+                <li>
+                  <h5>參、個人資料利用之期間、地區、對象與方式</h5>
+                  <ul>
+                    <li>
+                      一、本平台於蒐集目的之存續期間或因執行業務所需保存期間內,得合理利用您的個人資料,利用地區不限。
+                    </li>
+                    <li>
+                      二、本平台利用您的個人資料於蒐集目的宣告之各項業務執行,包括因業務執行所必須之各項聯繫與通知。
+                    </li>
+                    <li>
+                      三、本平台利用各項網路資源服務使用紀錄,進行總體流量、使用行為研究及應用,以提昇網站服務品質,不針對個別使用者分析。
+                    </li>
+                  </ul>
+                </li>
+
+                <li>
+                  <h5>肆、個人資料之提供</h5>
+                  <ul>
+                    <li>
+                      一、您可自由選擇是否提供相關個人資料,惟若拒絕提供個人資料,本平台將無法提供相關服務。
+                    </li>
+                    <li>
+                      二、請依各項服務需求提供您本人正確、完整的個人資料,若您的個人資料有任何異動,請主動於本平台修正。
+                    </li>
+                    <li>
+                      三、若您提供錯誤、過時、不完整或具誤導性的資料,而損及您的相關權益,本平台將不負相關賠償責任。
+                    </li>
+                  </ul>
+                </li>
+
+                <li>
+                  <h5>伍、個人資料之保密</h5>
+                  <p>
+                    本平台將善盡個人資料保護之責。如因天災、事變或其他不可抗力所致者,致您的個人資料被竊取、洩漏、竄改、遭其他侵害者,本平台將於查明後以電話、電子郵件或網站公告等方法,擇適當方式通知您。
+                  </p>
+                </li>
+
+                <li>
+                  <h5>陸、保證線上課程內容於開課後均符合下列事項:</h5>
+                  <ul>
+                    <li>
+                      內容未有錯誤、對第三人之人身攻擊、鼓吹暴力、仇恨或歧視特定及不特定族群之攻擊性內容、對身體產生生理傷害和依賴性之菸草製品及管制藥品及其用具、散播色情或具裸露之內容、違反公序良俗或其他經甲方判斷不當之內容。
+                    </li>
+                    <li>
+                      無侵害他人智慧財產權(包括但不限於著作權、商標權、專利權、營業秘密及其他專門技術)之情事。
+                    </li>
+                    <li>無任何置入性行銷之商業行為。</li>
+                    <li>無其他違反法令之情事。</li>
+                  </ul>
+                </li>
+
+                <li>
+                  <h5>柒、同意書之效力</h5>
+                  <ul>
+                    <li>
+                      一、當您開始使用本平台的功能及服務時,即表示您已閱讀瞭解並同意本同意書的內容。
+                    </li>
+                    <li>
+                      二、本平台保留隨時修改本同意書之權利,內容修改時將於本平台網站公告。如您未於公告後一個月內提出異議或仍繼續使用本平台相關服務,將視為您已同意並接受本平台所更改之內容。
+                    </li>
+                  </ul>
+                </li>
+              </ul>
+
+              <p class="mt-10 font-weight-bold">個人資料之同意提供</p>
+
+              <v-checkbox
+                v-model="consent_1"
+                label="一、本人已充分知悉上述告知事項。"
+                hide-details
+                color="purple"
+              ></v-checkbox>
+              <v-checkbox
+                v-model="consent_2"
+                label="二、本人同意本平台蒐集、處理、利用本人之個人資料執行業務管理與服務推廣相關業務目的之提供。"
+                hide-details
+                color="purple"
+              ></v-checkbox>
+            </v-form>
+          </v-card-text>
+        </v-window-item>
+
+        <v-window-item :value="2">
           <v-card-text>
             <v-form
               ref="schoolForm"
@@ -969,7 +1103,7 @@ function deleteEvent(index) {
           </v-card-text>
         </v-window-item>
 
-        <v-window-item :value="2">
+        <v-window-item :value="3">
           <v-card-text class="resume-content">
             <button class="resume-btn">
               <p class="d-flex flex-column align-center">
@@ -1447,7 +1581,7 @@ function deleteEvent(index) {
           </v-card-text> -->
         </v-window-item>
 
-        <v-window-item :value="3">
+        <v-window-item :value="4">
           <v-card-text class="mb-7">
             <v-form ref="courseForm" lazy-validation @submit.prevent>
               <v-row class="justify-space-evenly">
@@ -2060,7 +2194,10 @@ function deleteEvent(index) {
                           </v-card>
                         </v-dialog>
 
-                        <h6 v-if="eventData.list.length" class="table-title mt-10">
+                        <h6
+                          v-if="eventData.list.length"
+                          class="table-title mt-10"
+                        >
                           {{ t("session_info") }}
                         </h6>
 
@@ -2545,7 +2682,7 @@ function deleteEvent(index) {
           </v-card-text>
         </v-window-item>
 
-        <v-window-item :value="4">
+        <v-window-item :value="5">
           <v-card-text class="mb-7 finish-step">
             <p>
               {{ t("confirmation_messages.waiting_for_staff") }} <br />
@@ -2570,7 +2707,7 @@ function deleteEvent(index) {
         class="justify-center flex-column flex-sm-row my-5 mb-sm-0 mt-sm-7"
       >
         <v-btn
-          v-if="step > 1 && step !== 4"
+          v-if="step > 1 && step !== 5"
           color="gray"
           variant="outlined"
           size="large"
@@ -2582,7 +2719,7 @@ function deleteEvent(index) {
         <v-spacer></v-spacer>
 
         <v-btn
-          v-if="step < 3"
+          v-if="step < 4"
           color="purple"
           variant="flat"
           size="large"
@@ -2600,7 +2737,7 @@ function deleteEvent(index) {
             <span class="ms-2"> 尚有欄位未填寫 </span>
           </div> -->
         <v-btn
-          v-if="step === 3"
+          v-if="step === 4"
           color="purple"
           variant="flat"
           size="large"
@@ -2627,7 +2764,7 @@ function deleteEvent(index) {
           <span class="ms-2"> {{ t("form.course_time_not_filled") }} </span>
         </div>
         <v-btn
-          v-if="step === 4"
+          v-if="step === 5"
           color="purple"
           variant="outlined"
           class="mb-5 mb-sm-0 me-sm-3"
@@ -2636,7 +2773,7 @@ function deleteEvent(index) {
             t("return_to_home")
           }}</router-link>
         </v-btn>
-        <v-btn v-if="step === 4" color="purple" variant="flat">
+        <v-btn v-if="step === 5" color="purple" variant="flat">
           <router-link to="/user/courses" class="px-7">{{
             t("go_to_course_area")
           }}</router-link>
@@ -2803,11 +2940,28 @@ function deleteEvent(index) {
   }
 }
 
+.resume-content {
+  .table-title {
+    width: 100% !important;
+  }
+}
+
 input[type="checkbox"] {
   // accent-color: var(--purple);
   transform: scale(1.5);
 }
 
+.consent-form {
+  max-width: 800px;
+  font-size: 1rem;
+  line-height: 2.2;
+
+  h5 {
+    margin: 10px 0;
+    font-size: 1.125rem;
+  }
+}
+
 .school-form {
   max-width: 800px;
   margin: 50px auto 0;

+ 10 - 10
src/views/Courses/Proposal.vue

@@ -1198,7 +1198,7 @@ function removeTeacher(index) {
             <v-form ref="courseForm" lazy-validation @submit.prevent>
               <v-row class="justify-space-evenly">
                 <v-col cols="12" md="7">
-                  <v-label class="d-block mb-5">
+                  <!-- <v-label class="d-block mb-5">
                     <p class="pb-5 pe-3">
                       {{ t("form.course_name") }}<span class="mark">*</span>
                     </p>
@@ -1208,7 +1208,7 @@ function removeTeacher(index) {
                       density="compact"
                       variant="outlined"
                     ></v-text-field>
-                  </v-label>
+                  </v-label> -->
 
                   <v-label class="d-block mb-5">
                     <p class="pb-5 pe-3">
@@ -1288,7 +1288,7 @@ function removeTeacher(index) {
                     ></v-text-field>
                   </v-label> -->
 
-                  <v-label class="d-block mb-5">
+                  <!-- <v-label class="d-block mb-5">
                     <p class="pb-5 pe-3">
                       {{ t("form.course_price") }}<span class="mark">*</span>
                     </p>
@@ -1298,9 +1298,9 @@ function removeTeacher(index) {
                       density="compact"
                       variant="outlined"
                     ></v-text-field>
-                  </v-label>
+                  </v-label> -->
 
-                  <v-label class="d-block mb-5">
+                  <!-- <v-label class="d-block mb-5">
                     <p class="pb-5 pe-3">
                       {{ t("form.course_quota") }}<span class="mark">*</span>
                     </p>
@@ -1310,9 +1310,9 @@ function removeTeacher(index) {
                       variant="outlined"
                       placeholder="請輸入報名人數上限"
                     ></v-text-field>
-                  </v-label>
+                  </v-label> -->
 
-                  <v-label class="d-block mb-5">
+                  <!-- <v-label class="d-block mb-5">
                     <p class="pb-5 pe-3">
                       {{ t("form.minimum_enrollment")
                       }}<span class="mark">*</span>
@@ -1323,7 +1323,7 @@ function removeTeacher(index) {
                       variant="outlined"
                       :placeholder="t('form.enter_minimum_enrollment')"
                     ></v-text-field>
-                  </v-label>
+                  </v-label> -->
 
                   <v-label class="d-block mb-5">
                     <p class="pb-5 pe-3">
@@ -1355,7 +1355,7 @@ function removeTeacher(index) {
 
                   <v-label class="d-block">
                     <p class="d-flex">
-                      {{ t("form.upload_course_cover_image")
+                      {{ t("form.upload_portfolio_image")
                       }}<span class="mark">*</span>
                     </p>
 
@@ -1365,7 +1365,7 @@ function removeTeacher(index) {
                       ref="coverImgRef"
                       label="File input"
                       variant="outlined"
-                      :placeholder="t('form.choose_image')"
+                      :placeholder="t('form.portfolio_image')"
                       @change="handleCoverImg"
                       style="display: none"
                     ></v-file-input>

+ 2 - 2
src/views/Home.vue

@@ -346,7 +346,7 @@ let closeBanner = ref(false);
       class="cover"
     />
     <img
-      src="@/assets/img/home/logo.png"
+      src="@/assets/img/home/logo-center.png"
       alt="臺灣工藝學習平台"
       class="logo"
     />
@@ -902,7 +902,7 @@ let closeBanner = ref(false);
     object-fit: cover;
   }
   .logo {
-    width: 800px;
+    max-width: 600px;
     position: absolute;
     z-index: 10;
   }

+ 1 - 1
src/views/User/Courses.vue

@@ -2520,7 +2520,7 @@ async function closeClass(id) {
                     <v-card title="提示">
                       <v-card-text>
                         <p class="mb-2">確定要提交審核嗎?</p>
-                        <small>(請先至 Email 確認駁回原因並完成修正)</small>
+                        <small>(請先至 Email 確認原因並修正補充)</small>
                       </v-card-text>
 
                       <v-card-actions class="ma-2 justify-end">

+ 83 - 8
src/views/User/Passport.vue

@@ -90,6 +90,8 @@ function getBankCode(string, type) {
     return match ? match[1] : null;
   }
 }
+
+let materialsDialog = ref(false);
 </script>
 
 <template>
@@ -120,10 +122,7 @@ function getBankCode(string, type) {
           />
         </div>
         <div class="record-item">
-          <img
-            src="@/assets/img/passport/icon-03.png"
-            alt="臺灣工藝學習平台"
-          />
+          <img src="@/assets/img/passport/icon-03.png" alt="臺灣工藝學習平台" />
           <p>
             {{ t("learning_hours.total") }} <br />
             <strong> {{ store.userInfo.hours }} </strong>
@@ -148,16 +147,92 @@ function getBankCode(string, type) {
           />
         </div>
         <div class="record-item">
-          <img
-            src="@/assets/img/passport/icon-03.png"
-            alt="臺灣工藝學習平台"
-          />
+          <img src="@/assets/img/passport/icon-03.png" alt="臺灣工藝學習平台" />
           <p>
             {{ t("learning_hours.total") }} <br />
             <strong> {{ store.userInfo.points }} </strong>
             <small class="ps-2">{{ t("learning_hours.point") }}</small>
           </p>
         </div>
+
+        <v-btn
+          class="mt-3"
+          color="blue"
+          variant="tonal"
+          @click="materialsDialog = true"
+        >
+          兌換工藝材料包
+        </v-btn>
+
+        <v-dialog v-model="materialsDialog" width="500">
+          <v-card class="py-5">
+            <v-card-title class="ms-5">
+              兌換工藝材料包
+              <small class="d-block text-grey"
+                >累積 20 點即可兌換 1 份工藝材料包</small
+              >
+            </v-card-title>
+            <v-card-text>
+              <img src="@/assets/img/passport/materials.jpg" alt="" />
+              <v-form @submit.prevent>
+                <v-container>
+                  <v-row>
+                    <v-col cols="12">
+                      <v-text-field
+                        v-model="firstName"
+                        :rules="rules"
+                        label="收件人姓名"
+                        variant="outlined"
+                      ></v-text-field>
+                    </v-col>
+
+                    <v-col cols="12">
+                      <v-text-field
+                        v-model="firstName"
+                        :rules="rules"
+                        label="收件人電話"
+                        variant="outlined"
+                      ></v-text-field>
+                    </v-col>
+
+                    <v-col cols="12" sm="4">
+                      <v-text-field
+                        v-model="firstName"
+                        :rules="rules"
+                        label="郵遞區號"
+                        variant="outlined"
+                      ></v-text-field>
+                    </v-col>
+
+                    <v-col cols="12" sm="8">
+                      <v-text-field
+                        v-model="firstName"
+                        :rules="rules"
+                        label="收件人地址"
+                        variant="outlined"
+                      ></v-text-field>
+                    </v-col>
+                  </v-row>
+                </v-container>
+
+                <div class="d-flex align-center justify-center mt-7">
+                  <v-btn variant="text" @click="materialsDialog = false"
+                    >關閉</v-btn
+                  >
+                  <v-btn type="submit" class="ms-5" variant="flat" color="blue"
+                    >
+                    <span class="text-white">兌換</span></v-btn
+                  >
+                </div>
+              </v-form>
+            </v-card-text>
+            <!-- <v-card-actions>
+              <v-btn color="blue" block @click="materialsDialog = false"
+                >關閉</v-btn
+              >
+            </v-card-actions> -->
+          </v-card>
+        </v-dialog>
       </v-col>
       <v-col cols="12">
         <div class="d-flex flex-column flex-sm-row justify-center tab-btn mt-5">