CourseDetail.vue 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279
  1. <script setup>
  2. import { ref, reactive, computed, watch } from "vue";
  3. import { useMainStore } from "@/stores/store";
  4. import { useRoute } from "vue-router";
  5. import VueDatePicker from "@vuepic/vue-datepicker";
  6. import "@vuepic/vue-datepicker/dist/main.css";
  7. import axios from "axios";
  8. import moment from "moment";
  9. import Navbar from "@/components/Navbar.vue";
  10. import CourseCard from "@/components/CourseCard.vue";
  11. const route = useRoute();
  12. const store = useMainStore();
  13. const courseId = route.params.id; // 網址參數
  14. // signUpDialog
  15. let step = ref(1);
  16. // let signUpDialog = ref(false);
  17. let signUpDialog = reactive([]);
  18. let checkConsent = ref(false);
  19. watch(signUpDialog, (val) => {
  20. console.log("signUpDialog", val);
  21. });
  22. let groupName = ref("");
  23. let course = reactive({
  24. data: [],
  25. });
  26. let isInner = ref("");
  27. let isLoading = ref(false);
  28. let isSignUpLoading = ref(false);
  29. const breadcrumbs = reactive([
  30. {
  31. title: "首頁",
  32. disabled: false,
  33. href: "/",
  34. },
  35. {
  36. title: "探索課程",
  37. disabled: false,
  38. href: "/course-list",
  39. },
  40. {
  41. title: "課程清單",
  42. disabled: true,
  43. },
  44. ]);
  45. let courseNameId = ref(null);
  46. // 取得資料
  47. (async () => {
  48. try {
  49. const response = await axios.get(
  50. `https://cmm.ai:8088/api/get_class_name?class_name_id=${courseId}`
  51. );
  52. course.data = response.data.classes[0];
  53. isInner.value = course.data.is_inner;
  54. console.log("courseData", course.data);
  55. courseNameId.value = course.data.class_name_id;
  56. console.log(" courseNameId.value", courseNameId.value);
  57. getOtherClass();
  58. if (course.data.group_id === 1) {
  59. groupName.value = "未來工藝學群";
  60. } else if (course.data.group_id === 2) {
  61. groupName.value = "技藝工藝學群";
  62. } else if (course.data.group_id === 3) {
  63. groupName.value = "生活工藝學群";
  64. } else if (course.data.group_id === 4) {
  65. groupName.value = "青年工藝學群";
  66. } else if (course.data.group_id === 5) {
  67. groupName.value = "世代工藝學群";
  68. } else if (course.data.group_id === 6) {
  69. groupName.value = "修護工藝學群";
  70. } else if (course.data.group_id === 7) {
  71. groupName.value = "跨域工藝學群";
  72. } else if (course.data.group_id === 8) {
  73. groupName.value = "線上工藝學群";
  74. } else if (course.data.group_id === 9) {
  75. groupName.value = "綠工藝希望工程";
  76. }
  77. } catch (error) {
  78. console.error(error);
  79. }
  80. })();
  81. let session = reactive({
  82. data: [],
  83. });
  84. // 取得場次
  85. (async () => {
  86. try {
  87. const response = await axios.get(
  88. `https://cmm.ai:8088/api/get_event?class_name_id=${courseId}`
  89. );
  90. session.data = response.data.classes;
  91. console.log("session.data", session.data);
  92. console.log("response", response.data.classes[0]);
  93. } catch (error) {
  94. console.error(error);
  95. }
  96. })();
  97. let other = reactive({
  98. classes: [],
  99. });
  100. // 取得清單
  101. async function getOtherClass() {
  102. isLoading.value = true;
  103. // let number = Math.floor(Math.random() * 300) + 1; // 在 1-300 間取一個隨機正整數
  104. try {
  105. const response = await axios.get(
  106. `https://cmm.ai:8088/api/get_class_name?is_check=1&category=${course.data.category}&page_num=1&page_amount=4`
  107. );
  108. console.log("其他 response", response);
  109. other.classes = response.data.classes.filter(
  110. (item) => item.class_name_id !== courseNameId.value
  111. ); // 移除當前課程(保留3筆)
  112. // other.classes = response.data.classes;
  113. isLoading.value = false;
  114. } catch (error) {
  115. console.error(error);
  116. }
  117. }
  118. const dynamicCols = computed(() => {
  119. return isInner.value === 0 ? "5" : "8";
  120. });
  121. let user = reactive({
  122. name: "",
  123. user_name: "",
  124. birthday: "",
  125. gender: "",
  126. phone: "",
  127. address: "",
  128. });
  129. function closeSignUpDialog() {
  130. for (let index = 0; index < signUpDialog.length; index++) {
  131. signUpDialog[index] = false;
  132. }
  133. checkConsent.value = false;
  134. step.value = 1;
  135. }
  136. // 取得報名資料
  137. async function signUp(index) {
  138. let isLogin = store.checkToken();
  139. let token = store.token;
  140. console.log("isLogin", isLogin);
  141. if (isLogin && token) {
  142. signUpDialog[index] = true;
  143. let response = await axios.get(
  144. `https://cmm.ai:8088/api/get_user_information?get_all=0&get_detail_information=1&access_token=${token}`
  145. );
  146. console.log("response.data user_inform", response.data);
  147. Object.keys(response.data.user_inform[0]).forEach((key) => {
  148. if (user[key] !== undefined) {
  149. user[key] = response.data.user_inform[0][key];
  150. }
  151. });
  152. // 判斷手機格式
  153. const phoneSplit = response.data.user_inform[0].phone.split(" ");
  154. if (phoneSplit.length === 1) {
  155. user.phone = phoneSplit[0];
  156. } else if (phoneSplit.length === 2) {
  157. user.phone = phoneSplit[1];
  158. }
  159. console.log("response", response);
  160. } else {
  161. signUpDialog[index] = false;
  162. store.openLoginDialog();
  163. }
  164. }
  165. let birthdayError = ref(false);
  166. let resultCard = reactive({
  167. text: "",
  168. icon: "",
  169. color: "",
  170. });
  171. // 確認報名
  172. async function signUpSubmit(id) {
  173. let token = store.token;
  174. isSignUpLoading.value = true;
  175. if (!user.birthday || user.birthday === "") {
  176. birthdayError.value = true;
  177. // isSignUpLoading.value = false;
  178. } else {
  179. birthdayError.value = false;
  180. // isSignUpLoading.value = false;
  181. }
  182. // 檢查欄位是否有空值
  183. for (const value of Object.values(user)) {
  184. if (!value) {
  185. isSignUpLoading.value = false;
  186. return;
  187. }
  188. }
  189. console.log("token", token);
  190. const formData = new FormData();
  191. formData.append("event_id", id);
  192. try {
  193. const response = await axios.post(
  194. `https://cmm.ai:8088/api/input_registration?access_token=${token}`,
  195. formData
  196. );
  197. console.log("確認報名", response);
  198. isSignUpLoading.value = false;
  199. if (response.data.msg === "success") {
  200. resultCard.text = "報名成功!";
  201. resultCard.icon = "mdi-check-circle";
  202. resultCard.color = "success";
  203. step.value++;
  204. } else if (response.data.msg === "already registrate") {
  205. resultCard.text = "已報名過此課程";
  206. resultCard.icon = "mdi-information";
  207. resultCard.color = "info";
  208. step.value++;
  209. } else {
  210. resultCard.text = response.data.msg;
  211. resultCard.icon = "mdi-information";
  212. resultCard.color = "info";
  213. step.value++;
  214. }
  215. // if (response.data.msg === "already registrate") {
  216. // resultCard.text = "此課程已報名";
  217. // resultCard.icon = "mdi-information";
  218. // resultCard.color = "info";
  219. // step.value++;
  220. // } else if (response.data.msg === "class is full") {
  221. // resultCard.text = "報名已額滿";
  222. // resultCard.icon = "mdi-information";
  223. // resultCard.color = "info";
  224. // step.value++;
  225. // } else if (response.data.code === 200) {
  226. // resultCard.text = "報名成功!";
  227. // resultCard.icon = "mdi-check-circle";
  228. // resultCard.color = "success";
  229. // step.value++;
  230. // }
  231. } catch (error) {
  232. console.error(error);
  233. }
  234. }
  235. // 開啟登入視窗
  236. // function openLoginDialog() {
  237. // store.loginDialog = true;
  238. // }
  239. let currentTitle = computed(() => {
  240. switch (step.value) {
  241. case 1:
  242. return "報名同意書";
  243. case 2:
  244. return "課程報名表";
  245. }
  246. });
  247. // 判斷報名時間是否已截止
  248. function isDateExpired(dateString) {
  249. const registrationEndDate = new Date(dateString);
  250. // 獲取當前時間
  251. const currentDate = new Date();
  252. console.log("registrationEndDate", registrationEndDate);
  253. console.log("currentDate", currentDate);
  254. if (registrationEndDate > currentDate) {
  255. return true; // 日期已過期
  256. } else {
  257. return false; // 日期未過期
  258. }
  259. }
  260. </script>
  261. <template>
  262. <Navbar />
  263. <v-container class="mt-16 course-detail">
  264. <v-breadcrumbs
  265. :items="breadcrumbs"
  266. divider="/"
  267. class="mt-3 mb-16 pa-0"
  268. ></v-breadcrumbs>
  269. <v-row class="justify-center">
  270. <v-col cols="3" class="title pa-0">
  271. <img
  272. src="@/assets/img/course/detail-background.png"
  273. alt="臺灣工藝學校全球學習共享平台"
  274. class="bg-img"
  275. />
  276. <h2>{{ course.data.name }}</h2>
  277. </v-col>
  278. <v-col :cols="dynamicCols" class="pa-0 d-flex justify-center">
  279. <img
  280. :src="store.getImageSrc(course.data)"
  281. alt="臺灣工藝學校全球學習共享平台"
  282. class="cover-img"
  283. :class="{ small: isInner === 0 }"
  284. />
  285. </v-col>
  286. <v-col cols="12">
  287. <div class="info">
  288. <table>
  289. <thead>
  290. <tr>
  291. <th colspan="2">課程資訊</th>
  292. </tr>
  293. </thead>
  294. <tbody>
  295. <tr>
  296. <td>課程主題</td>
  297. <td>{{ course.data.name }}</td>
  298. </tr>
  299. <tr>
  300. <td>課程簡介</td>
  301. <td v-html="store.formatString(course.data.introduction)"></td>
  302. </tr>
  303. <tr v-show="course.data.category !== ''">
  304. <td>課程類別</td>
  305. <td>
  306. {{
  307. course.data.group_id === 9
  308. ? course.data.group_sort
  309. : course.data.category
  310. }}
  311. </td>
  312. </tr>
  313. <tr>
  314. <td>課程地點</td>
  315. <td>{{ course.data.address }}</td>
  316. </tr>
  317. <tr v-if="course.data.organizer !== ''">
  318. <td>主辦單位</td>
  319. <td>
  320. {{
  321. course.data.group_id === 9
  322. ? course.data.location_name
  323. : course.data.organizer
  324. }}
  325. </td>
  326. </tr>
  327. <!-- <tr>
  328. <td>課程地點</td>
  329. <td>{{ course.data.address }}</td>
  330. </tr> -->
  331. <tr>
  332. <td>所屬學群</td>
  333. <td>{{ groupName }}</td>
  334. </tr>
  335. <!-- <tr>
  336. <td>報名對象</td>
  337. <td></td>
  338. </tr> -->
  339. <!-- <tr>
  340. <td>收費方式</td>
  341. <td></td>
  342. </tr> -->
  343. <!-- <tr>
  344. <td>報名方式</td>
  345. <td></td>
  346. </tr> -->
  347. <tr
  348. v-if="
  349. isInner !== 0 &&
  350. session.data.length &&
  351. session.data[0]?.contact !== '' &&
  352. course.data.group_id !== 9
  353. "
  354. >
  355. <td>聯絡方式</td>
  356. <td>{{ session.data[0]?.contact }}</td>
  357. </tr>
  358. <tr
  359. v-if="
  360. isInner !== 0 &&
  361. session.data.length &&
  362. session.data[0]?.fee_payment !== null
  363. "
  364. >
  365. <td>繳費方式</td>
  366. <td>
  367. {{ session.data[0]?.fee_payment }}
  368. <span
  369. v-if="session.data[0]?.fee_payment === '匯款'"
  370. class="text-red"
  371. >
  372. (匯款後請主動來電告知姓名與帳號後五碼)
  373. </span>
  374. </td>
  375. </tr>
  376. <tr v-if="isInner === 0">
  377. <td>備註</td>
  378. <td>此課程不會登錄至學習護照中</td>
  379. </tr>
  380. </tbody>
  381. </table>
  382. <div class="d-flex justify-end">
  383. <v-btn v-if="isInner === 0" rounded="xl" color="brown">
  384. <a :href="session.data[0]?.URL" target="_blank"
  385. >觀看課程資訊與報名</a
  386. >
  387. </v-btn>
  388. <!-- <v-btn v-else rounded="xl" color="brown">附件下載</v-btn> -->
  389. </div>
  390. </div>
  391. </v-col>
  392. <v-col
  393. cols="12"
  394. v-if="isInner !== 0 && session.data.length"
  395. :class="{
  396. hide: course.data.special_class_list_name === 'one_day_class',
  397. }"
  398. >
  399. <div class="sessions mb-16">
  400. <table>
  401. <thead>
  402. <tr>
  403. <th>場次名稱</th>
  404. <th>開始/結束時間</th>
  405. <th v-show="session.data[0].location" style="width: 200px">
  406. 課程教室
  407. </th>
  408. <th>課程費用</th>
  409. <th>課程名額</th>
  410. <th>講師</th>
  411. <th>報名截止日</th>
  412. <th></th>
  413. </tr>
  414. </thead>
  415. <tbody>
  416. <tr v-for="(item, index) in session.data" :key="index">
  417. <td width="20%">{{ item.class_name }}</td>
  418. <td width="20%">
  419. {{
  420. moment(`${item.start_time}`)
  421. .utcOffset(0)
  422. .format("YYYY/MM/DD HH:mm")
  423. }}
  424. <br />
  425. ~ <br />
  426. {{
  427. moment(`${item.end_time}`)
  428. .utcOffset(0)
  429. .format("YYYY/MM/DD HH:mm")
  430. }}
  431. </td>
  432. <td v-show="item.location !== ''">
  433. {{ item.location }}
  434. </td>
  435. <td>{{ item.fee_method }}</td>
  436. <td>{{ item.number_limit }}</td>
  437. <td>{{ item.lecturer }}</td>
  438. <td>
  439. <span
  440. v-if="item.registration_end && item.registration_end !== ''"
  441. >
  442. {{
  443. moment(`${item.registration_end}`)
  444. .utcOffset(0)
  445. .format("YYYY/MM/DD HH:mm")
  446. }}
  447. </span>
  448. <span v-else>無</span>
  449. </td>
  450. <td>
  451. <v-btn v-if="isInner === 0" rounded="xl" color="brown">
  452. <a :href="item.URL" target="_blank">報名</a>
  453. </v-btn>
  454. <v-dialog
  455. v-else
  456. width="auto"
  457. class="pa-0 signup-dialog"
  458. scrollable
  459. persistent
  460. >
  461. <template v-slot:activator="{ props }">
  462. <v-btn
  463. v-if="isDateExpired(item.registration_end)"
  464. @click="signUp(index)"
  465. v-bind="props"
  466. text="報名"
  467. rounded="xl"
  468. color="brown"
  469. >
  470. </v-btn>
  471. <v-btn
  472. v-else
  473. text="已截止"
  474. rounded="xl"
  475. color="brown"
  476. disabled
  477. >
  478. </v-btn>
  479. </template>
  480. <template v-slot:default="{ isActive }">
  481. <v-card class="mx-auto pa-3" max-width="1000">
  482. <v-card-title
  483. class="text-h6 font-weight-regular justify-space-between"
  484. >
  485. <v-avatar
  486. v-if="step !== 3"
  487. color="primary"
  488. size="26"
  489. v-text="step"
  490. class="me-3"
  491. ></v-avatar>
  492. <span>{{ currentTitle }}</span>
  493. </v-card-title>
  494. <v-window v-model="step">
  495. <v-window-item :value="1">
  496. <v-card-text>
  497. <section class="consent">
  498. <h4 class="text-center">個人資料使用同意書</h4>
  499. <p>
  500. 臺灣工藝學校全球學習共享平台〈以下簡稱工藝中心〉因辦理
  501. <span class="font-weight-medium"
  502. >【{{ item.class_name }}】</span
  503. >
  504. 業務而獲取您的個人資料:姓名、連絡方式〈包括但不限於電話號碼、E-MAIL、居住或工作地址〉等得以直接或間接識別您個人之資料。若您所提供之個人資料,經檢舉或工藝中心發現不足以確認您的身分真實性或其他個人資料冒用、盜用、資料不實等情形,工藝中心有權暫時停止提供對您的服務。
  505. </p>
  506. <p>
  507. 工藝中心將基於個人資料保護法及相關法令之規定下,依隱私權保護政策,蒐集、處理及利用您的個人資料。您同意工藝中心以您所提供的個人資料確認您的身份、與您進行連絡、提供您相關服務及資訊,以及其他隱私權保護政策規範之使用方式。工藝中心針對您的個人資料利用之期間,自您簽署同意起至您請求刪除個資為止。
  508. </p>
  509. <p>
  510. 您可依個人資料保護法,就您的個人資料向工藝中心進行查詢或請求閱覽、請求給複製本、請求補充或更正、請求停止蒐集及處理與利用、請求刪除。但因您行使上述權利而導致工藝中心相關業務對您的權益產生減損時,工藝中心不負相關賠償責任。
  511. </p>
  512. </section>
  513. <v-checkbox
  514. v-model="checkConsent"
  515. label="我已完全閱讀並同意以上內容"
  516. color="brown"
  517. ></v-checkbox>
  518. </v-card-text>
  519. </v-window-item>
  520. <v-window-item :value="2">
  521. <v-card-text class="pa-0">
  522. <v-sheet class="mx-auto user-form">
  523. <v-form @submit.prevent>
  524. <h2
  525. class="text-center font-weight-medium mb-8"
  526. >
  527. 【{{ item.class_name }}】
  528. </h2>
  529. <v-label class="d-block">
  530. <p class="d-flex mb-5">
  531. 真實姓名<span class="mark">*</span>
  532. </p>
  533. <v-text-field
  534. v-model="user.name"
  535. :rules="[
  536. (v) => !!v || '請輸入您的真實姓名',
  537. ]"
  538. density="compact"
  539. variant="outlined"
  540. ></v-text-field>
  541. </v-label>
  542. <v-label class="mt-1">
  543. <p class="d-flex mb-5">
  544. 生日<span class="mark">*</span>
  545. </p>
  546. </v-label>
  547. <VueDatePicker
  548. v-model="user.birthday"
  549. :format="store.datePickerFormat"
  550. :enable-time-picker="false"
  551. :max-date="new Date()"
  552. locale="cn"
  553. ></VueDatePicker>
  554. <span
  555. v-if="birthdayError"
  556. class="birthday-error"
  557. >請選擇您的生日</span
  558. >
  559. <v-label class="d-block mt-5">
  560. <p class="d-flex mb-5">
  561. 手機號碼<span class="mark">*</span>
  562. </p>
  563. <v-text-field
  564. v-model="user.phone"
  565. :rules="[
  566. (v) => !!v || '請輸入您的手機號碼',
  567. ]"
  568. density="compact"
  569. variant="outlined"
  570. ></v-text-field>
  571. </v-label>
  572. <v-label class="d-block mt-1">
  573. <p class="d-flex mb-3">
  574. 性別<span class="mark">*</span>
  575. </p>
  576. <v-radio-group v-model="user.gender" inline>
  577. <v-radio
  578. label="男"
  579. value="男"
  580. color="brown"
  581. ></v-radio>
  582. <v-radio
  583. label="女"
  584. value="女"
  585. class="mx-2"
  586. color="brown"
  587. ></v-radio>
  588. <v-radio
  589. label="多元"
  590. value="多元"
  591. color="brown"
  592. ></v-radio>
  593. </v-radio-group>
  594. </v-label>
  595. <v-label class="d-block">
  596. <p class="d-flex mb-5">
  597. 地址<span class="mark">*</span>
  598. </p>
  599. <v-text-field
  600. v-model="user.address"
  601. :rules="[(v) => !!v || '請輸入您的地址']"
  602. density="compact"
  603. variant="outlined"
  604. ></v-text-field>
  605. </v-label>
  606. <v-label class="d-block">
  607. <p class="d-flex mb-5">備註</p>
  608. <v-textarea
  609. placeholder="如有特殊需求或有需攜伴,請在備註欄告知老師"
  610. variant="outlined"
  611. rows="2"
  612. ></v-textarea>
  613. </v-label>
  614. <v-row class="mt-3">
  615. <v-col cols="6">
  616. <v-btn
  617. rounded
  618. class="w-100"
  619. variant="outlined"
  620. color="brown"
  621. @click="
  622. isActive.value = false;
  623. step = 1;
  624. "
  625. >取消</v-btn
  626. >
  627. </v-col>
  628. <v-col cols="6">
  629. <v-btn
  630. rounded
  631. type="submit"
  632. class="w-100"
  633. variant="flat"
  634. color="brown"
  635. :loading="isSignUpLoading"
  636. @click="signUpSubmit(item.event_id)"
  637. >確定報名</v-btn
  638. >
  639. </v-col>
  640. </v-row>
  641. </v-form>
  642. </v-sheet>
  643. </v-card-text>
  644. </v-window-item>
  645. <v-window-item :value="3">
  646. <v-card-text class="px-0 px-sm-2">
  647. <v-sheet
  648. width="300"
  649. class="text-center mx-auto result-card"
  650. >
  651. <v-icon
  652. class="mb-5"
  653. :color="resultCard.color"
  654. :icon="resultCard.icon"
  655. size="112"
  656. ></v-icon>
  657. <h2 class="text-h5 mb-6">
  658. {{ resultCard.text }}
  659. </h2>
  660. <v-divider class="mb-7"></v-divider>
  661. <v-btn
  662. class="text-none me-3"
  663. rounded
  664. width="90"
  665. color="brown"
  666. variant="outlined"
  667. @click="
  668. isActive.value = false;
  669. step = 1;
  670. "
  671. >
  672. 關閉
  673. </v-btn>
  674. <v-btn
  675. color="brown"
  676. class="text-none"
  677. rounded
  678. @click="isActive.value = false"
  679. >
  680. <router-link to="/user/passport"
  681. >前往會員專區</router-link
  682. >
  683. </v-btn>
  684. </v-sheet>
  685. </v-card-text>
  686. </v-window-item>
  687. </v-window>
  688. <v-divider v-if="step === 1"></v-divider>
  689. <v-card-actions v-if="step === 1" class="mt-2">
  690. <!-- <v-btn v-if="step > 1" variant="text" @click="step--">
  691. 上一步
  692. </v-btn> -->
  693. <v-spacer></v-spacer>
  694. <v-btn
  695. class="me-2 px-5"
  696. color="brown"
  697. variant="outlined"
  698. @click="isActive.value = false"
  699. >
  700. 不同意
  701. </v-btn>
  702. <v-btn
  703. class="px-5"
  704. color="brown"
  705. variant="flat"
  706. :disabled="!checkConsent"
  707. @click="step++"
  708. >
  709. 我同意
  710. </v-btn>
  711. </v-card-actions>
  712. </v-card>
  713. </template>
  714. </v-dialog>
  715. <!-- <v-btn rounded="xl" color="brown">
  716. <a v-if="isInner === 0" :href="item.URL" target="_blank"
  717. >報名</a
  718. >
  719. <v-dialog
  720. v-else
  721. v-model="signUpDialog[index]"
  722. width="auto"
  723. scrollable
  724. persistent
  725. class="pa-0 signup-dialog"
  726. >
  727. <template v-slot:activator="{ props }">
  728. <p v-bind="props" @click="signUp(index)">報名</p>
  729. </template>
  730. <template v-slot:default="{ isActive }">
  731. <v-card class="mx-auto pa-3" max-width="1000">
  732. <v-card-title
  733. class="text-h6 font-weight-regular justify-space-between"
  734. >
  735. <v-avatar
  736. v-if="step !== 3"
  737. color="primary"
  738. size="26"
  739. v-text="step"
  740. class="me-3"
  741. ></v-avatar>
  742. <span>{{ currentTitle }}</span>
  743. </v-card-title>
  744. <v-window v-model="step">
  745. <v-window-item :value="1">
  746. <v-card-text>
  747. <section class="consent">
  748. <h4 class="text-center">
  749. 個人資料使用同意書
  750. </h4>
  751. <p>
  752. 臺灣工藝學校全球學習共享平台〈以下簡稱工藝中心〉因辦理
  753. <span class="font-weight-medium"
  754. >【{{ item.class_name }}】</span
  755. >
  756. 業務而獲取您的個人資料:姓名、連絡方式〈包括但不限於電話號碼、E-MAIL、居住或工作地址〉等得以直接或間接識別您個人之資料。若您所提供之個人資料,經檢舉或工藝中心發現不足以確認您的身分真實性或其他個人資料冒用、盜用、資料不實等情形,工藝中心有權暫時停止提供對您的服務。
  757. </p>
  758. <p>
  759. 工藝中心將基於個人資料保護法及相關法令之規定下,依隱私權保護政策,蒐集、處理及利用您的個人資料。您同意工藝中心以您所提供的個人資料確認您的身份、與您進行連絡、提供您相關服務及資訊,以及其他隱私權保護政策規範之使用方式。工藝中心針對您的個人資料利用之期間,自您簽署同意起至您請求刪除個資為止。
  760. </p>
  761. <p>
  762. 您可依個人資料保護法,就您的個人資料向工藝中心進行查詢或請求閱覽、請求給複製本、請求補充或更正、請求停止蒐集及處理與利用、請求刪除。但因您行使上述權利而導致工藝中心相關業務對您的權益產生減損時,工藝中心不負相關賠償責任。
  763. </p>
  764. </section>
  765. <v-checkbox
  766. v-model="checkConsent"
  767. label="我已完全閱讀並同意以上內容"
  768. color="brown"
  769. ></v-checkbox>
  770. </v-card-text>
  771. </v-window-item>
  772. <v-window-item :value="2">
  773. <v-card-text class="pa-0">
  774. <v-sheet class="mx-auto user-form">
  775. <v-form @submit.prevent>
  776. <h2
  777. class="text-center font-weight-medium mb-8"
  778. >
  779. 【{{ item.class_name }}】
  780. </h2>
  781. <v-label class="d-block">
  782. <p class="d-flex mb-5">
  783. 真實姓名<span class="mark">*</span>
  784. </p>
  785. <v-text-field
  786. v-model="user.name"
  787. :rules="[
  788. (v) => !!v || '請輸入您的真實姓名',
  789. ]"
  790. density="compact"
  791. variant="outlined"
  792. ></v-text-field>
  793. </v-label>
  794. <v-label class="mt-1">
  795. <p class="d-flex mb-5">
  796. 生日<span class="mark">*</span>
  797. </p>
  798. </v-label>
  799. <VueDatePicker
  800. v-model="user.birthday"
  801. :format="store.datePickerFormat"
  802. :enable-time-picker="false"
  803. :max-date="new Date()"
  804. locale="cn"
  805. ></VueDatePicker>
  806. <span
  807. v-if="birthdayError"
  808. class="birthday-error"
  809. >請選擇您的生日</span
  810. >
  811. <v-label class="d-block mt-5">
  812. <p class="d-flex mb-5">
  813. 手機號碼<span class="mark">*</span>
  814. </p>
  815. <v-text-field
  816. v-model="user.phone"
  817. :rules="[
  818. (v) => !!v || '請輸入您的手機號碼',
  819. ]"
  820. density="compact"
  821. variant="outlined"
  822. ></v-text-field>
  823. </v-label>
  824. <v-label class="d-block mt-1">
  825. <p class="d-flex mb-3">
  826. 性別<span class="mark">*</span>
  827. </p>
  828. <v-radio-group
  829. v-model="user.gender"
  830. inline
  831. >
  832. <v-radio
  833. label="男"
  834. value="男"
  835. color="brown"
  836. ></v-radio>
  837. <v-radio
  838. label="女"
  839. value="女"
  840. class="mx-2"
  841. color="brown"
  842. ></v-radio>
  843. <v-radio
  844. label="多元"
  845. value="多元"
  846. color="brown"
  847. ></v-radio>
  848. </v-radio-group>
  849. </v-label>
  850. <v-label class="d-block">
  851. <p class="d-flex mb-5">
  852. 地址<span class="mark">*</span>
  853. </p>
  854. <v-text-field
  855. v-model="user.address"
  856. :rules="[
  857. (v) => !!v || '請輸入您的地址',
  858. ]"
  859. density="compact"
  860. variant="outlined"
  861. ></v-text-field>
  862. </v-label>
  863. <v-label class="d-block">
  864. <p class="d-flex mb-5">備註</p>
  865. <v-textarea
  866. placeholder="如有特殊需求或有需攜伴,請在備註欄告知老師"
  867. variant="outlined"
  868. rows="2"
  869. ></v-textarea>
  870. </v-label>
  871. <v-row class="mt-3">
  872. <v-col cols="6">
  873. <v-btn
  874. rounded
  875. class="w-100"
  876. variant="outlined"
  877. color="brown"
  878. @click="closeSignUpDialog()"
  879. >取消</v-btn
  880. >
  881. </v-col>
  882. <v-col cols="6">
  883. <v-btn
  884. rounded
  885. type="submit"
  886. class="w-100"
  887. variant="flat"
  888. color="brown"
  889. :loading="isSignUpLoading"
  890. @click="signUpSubmit(item.event_id)"
  891. >確定報名</v-btn
  892. >
  893. </v-col>
  894. </v-row>
  895. </v-form>
  896. </v-sheet>
  897. </v-card-text>
  898. </v-window-item>
  899. <v-window-item :value="3">
  900. <v-card-text class="px-0 px-sm-2">
  901. <v-sheet
  902. width="300"
  903. class="text-center mx-auto result-card"
  904. >
  905. <v-icon
  906. class="mb-5"
  907. :color="resultCard.color"
  908. :icon="resultCard.icon"
  909. size="112"
  910. ></v-icon>
  911. <h2 class="text-h5 mb-6">
  912. {{ resultCard.text }}
  913. </h2>
  914. <v-divider class="mb-7"></v-divider>
  915. <v-btn
  916. class="text-none me-3"
  917. rounded
  918. width="90"
  919. color="brown"
  920. variant="outlined"
  921. @click="closeSignUpDialog()"
  922. >
  923. 關閉
  924. </v-btn>
  925. <v-btn
  926. color="brown"
  927. class="text-none"
  928. rounded
  929. @click="closeSignUpDialog()"
  930. >
  931. <router-link to="/user/passport"
  932. >前往會員專區</router-link
  933. >
  934. </v-btn>
  935. </v-sheet>
  936. </v-card-text>
  937. </v-window-item>
  938. </v-window>
  939. <v-divider v-if="step === 1"></v-divider>
  940. <v-card-actions v-if="step === 1" class="mt-2">
  941. <v-spacer></v-spacer>
  942. <v-btn
  943. class="me-2 px-5"
  944. color="brown"
  945. variant="outlined"
  946. @click="signUpDialog[index] = false"
  947. >
  948. 不同意
  949. </v-btn>
  950. <v-btn
  951. class="px-5"
  952. color="brown"
  953. variant="flat"
  954. :disabled="!checkConsent"
  955. @click="step++"
  956. >
  957. 我同意
  958. </v-btn>
  959. </v-card-actions>
  960. </v-card>
  961. </template>
  962. </v-dialog>
  963. </v-btn> -->
  964. </td>
  965. <!-- <td style="width: 0; padding: 0; border: none">
  966. <div class="signup-btn">
  967. <v-btn rounded="xl" color="brown">
  968. <span @click="signUp()">點我報名 a</span>
  969. </v-btn>
  970. </div>
  971. </td> -->
  972. </tr>
  973. </tbody>
  974. </table>
  975. </div>
  976. </v-col>
  977. <v-col v-if="other.classes.length" cols="12" class="mb-16">
  978. <p class="other-title">其他推薦課程</p>
  979. <div class="d-flex justify-center mb-10" v-if="isLoading">
  980. <v-progress-circular
  981. color="grey-lighten-4"
  982. indeterminate
  983. ></v-progress-circular>
  984. </div>
  985. <v-row>
  986. <v-col
  987. sm="6"
  988. md="4"
  989. cols="12"
  990. v-for="(item, index) in other.classes"
  991. :key="index"
  992. class="pa-5"
  993. >
  994. <CourseCard :data="item" />
  995. </v-col>
  996. </v-row>
  997. </v-col>
  998. </v-row>
  999. </v-container>
  1000. </template>
  1001. <style lang="scss">
  1002. .v-window {
  1003. overflow-y: auto;
  1004. }
  1005. .hide {
  1006. display: none !important;
  1007. }
  1008. .course-detail {
  1009. .cover-img {
  1010. // width: 100%;
  1011. height: 100%;
  1012. max-height: 25em;
  1013. object-fit: cover;
  1014. &.small {
  1015. width: auto;
  1016. height: auto;
  1017. }
  1018. }
  1019. .title {
  1020. position: relative;
  1021. display: flex;
  1022. flex-direction: column;
  1023. align-items: center;
  1024. justify-content: center;
  1025. h2 {
  1026. margin-top: -2.1875em;
  1027. margin-left: 0.3125em;
  1028. font-size: 1.5em;
  1029. font-weight: 500;
  1030. line-height: 2em;
  1031. letter-spacing: 0.0625em;
  1032. color: #201715;
  1033. }
  1034. .bg-img {
  1035. width: 15.625em;
  1036. position: absolute;
  1037. z-index: -1;
  1038. right: -0.9375em;
  1039. bottom: -6.25em;
  1040. }
  1041. }
  1042. .info {
  1043. margin: 3.125em auto;
  1044. padding: 1.5625em 3.125em;
  1045. table {
  1046. td {
  1047. padding: 25px 25px 0;
  1048. line-height: 2em;
  1049. letter-spacing: 0.0625em;
  1050. vertical-align: top;
  1051. white-space: pre-line; // 顯示換行標籤
  1052. &:first-child {
  1053. border-right: 0.0625em solid #333;
  1054. }
  1055. strong {
  1056. font-weight: 500;
  1057. }
  1058. }
  1059. th {
  1060. padding-bottom: 1.25em;
  1061. border-bottom: 0.0625em solid #333;
  1062. }
  1063. th,
  1064. td:first-child {
  1065. width: 7.3125em;
  1066. font-size: 1.125em;
  1067. font-weight: 500;
  1068. }
  1069. }
  1070. }
  1071. .info,
  1072. .sessions {
  1073. background-color: #f2f2f4;
  1074. table {
  1075. width: 100%;
  1076. border-collapse: collapse;
  1077. td {
  1078. line-height: 1.875em;
  1079. }
  1080. }
  1081. }
  1082. .sessions {
  1083. padding: 1.5em 1.5em 0.5em;
  1084. letter-spacing: 0.1em;
  1085. overflow-x: scroll;
  1086. table {
  1087. width: 100%;
  1088. position: relative;
  1089. @media (max-width: 1280px) {
  1090. width: 1000px;
  1091. }
  1092. th {
  1093. width: 7.3125em;
  1094. font-size: 1.125em;
  1095. font-weight: 500;
  1096. padding-bottom: 1.25em;
  1097. }
  1098. td {
  1099. padding: 1em;
  1100. text-align: center;
  1101. vertical-align: middle;
  1102. }
  1103. tbody {
  1104. background-color: #fff;
  1105. }
  1106. }
  1107. .signup-btn {
  1108. position: absolute;
  1109. bottom: -3.3125em;
  1110. right: 0;
  1111. }
  1112. }
  1113. @media (max-width: 960px) {
  1114. width: 100%;
  1115. }
  1116. .other-title {
  1117. margin: 1.875em 0;
  1118. padding-bottom: 1.875em;
  1119. font-size: 1.625em;
  1120. text-align: center;
  1121. border-bottom: 0.0625em solid;
  1122. }
  1123. }
  1124. .user-form {
  1125. width: 37.5em;
  1126. padding: 0 1.875em 1.875em;
  1127. // box-shadow: 0.0625em 0.0625em 0.5em #ccc;
  1128. // border-radius: 0.3125em;
  1129. // margin: 0 0.9375em 0.9375em;
  1130. @media (max-width: 600px) {
  1131. width: 100%;
  1132. padding: 0;
  1133. }
  1134. h2 {
  1135. font-size: 1.125em;
  1136. }
  1137. .result-card {
  1138. position: absolute;
  1139. top: 50%;
  1140. left: 50%;
  1141. transform: translate(-50%, -50%);
  1142. }
  1143. .birthday-error {
  1144. display: block;
  1145. font-size: 0.75em;
  1146. color: #b00020;
  1147. margin-left: 0.9375em;
  1148. padding-top: 0.3125em;
  1149. }
  1150. }
  1151. .signup-dialog {
  1152. .v-card-text {
  1153. @media (max-width: 600px) {
  1154. overflow-y: auto;
  1155. max-height: 31.25em;
  1156. }
  1157. }
  1158. }
  1159. .consent {
  1160. max-width: 50em;
  1161. padding: 1.875em 1.25em;
  1162. border: 0.0625em solid #909090;
  1163. border-radius: 0.3125em;
  1164. h4 {
  1165. font-size: 1.125em;
  1166. font-weight: 400;
  1167. margin-bottom: 1.25em;
  1168. }
  1169. p {
  1170. margin-top: 1.25em;
  1171. font-size: 1em;
  1172. line-height: 1.625em;
  1173. }
  1174. }
  1175. </style>