Qrcode.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. <script setup lang="ts">
  2. import { ref, reactive, computed, onMounted } from "vue";
  3. import { useMainStore } from "@/stores/main";
  4. import { storeToRefs } from "pinia";
  5. import { useDisplay } from "vuetify";
  6. import { useRoute } from "vue-router";
  7. import { useI18n } from "vue-i18n";
  8. import { googleTokenLogin, decodeCredential } from "vue3-google-login";
  9. import type { CallbackTypes } from "vue3-google-login";
  10. // import Navbar from "@/components/Navbar.vue";
  11. import Dialog from "@/components/Dialog.vue";
  12. const mainStore = useMainStore();
  13. // variable
  14. const { t } = useI18n();
  15. const route = useRoute();
  16. const { name } = useDisplay();
  17. const email = ref("");
  18. const password = ref("");
  19. let ser_no: any = ref("");
  20. let showPassword = ref(false);
  21. let loginState = ref(false);
  22. let loading = ref(false);
  23. let time = ref(0);
  24. let dialog = reactive({
  25. msg: "",
  26. state: "",
  27. show: false,
  28. icon: "check_circle",
  29. });
  30. // getter
  31. const width = computed(() => {
  32. switch (name.value) {
  33. case "xs":
  34. return 12;
  35. case "sm":
  36. return 8;
  37. }
  38. });
  39. function setDialog(status: Boolean, msg: String = "") {
  40. if (status) {
  41. dialog.show = true;
  42. dialog.state = "success";
  43. dialog.msg =
  44. "儲值成功!<br/>已獲得價值 1000 元的 120 秒影片製作時間<br/>(儲值成功後即可登入電腦版進行影片製作)";
  45. dialog.icon = "check_circle";
  46. if (mainStore.userProfile?.available_time) {
  47. mainStore.userProfile.available_time =
  48. mainStore.userProfile.available_time + time.value;
  49. }
  50. } else {
  51. dialog.show = true;
  52. dialog.state = "error";
  53. dialog.msg = `${msg}`;
  54. dialog.icon = "highlight_off";
  55. }
  56. }
  57. // action
  58. async function submit() {
  59. loading.value = true;
  60. if (email.value === "" || password.value === "") {
  61. loading.value = false;
  62. return;
  63. }
  64. let response = await mainStore.qrLogIn(
  65. email.value,
  66. password.value,
  67. ser_no.value
  68. );
  69. loading.value = false;
  70. if (response?.data.time_added === -1) {
  71. setDialog(false, "此序號無效");
  72. } else if (response?.status === 200) {
  73. time.value = response?.data.time_added;
  74. setTimeout(() => {
  75. setDialog(true);
  76. }, 500);
  77. }
  78. }
  79. // lifecycle
  80. onMounted(() => {
  81. console.log("onMounted");
  82. if (route.query["add_time_code"]) {
  83. ser_no.value = route.query["add_time_code"];
  84. }
  85. mainStore.qrCheckLoggedIn();
  86. if (mainStore.token) {
  87. checkCode("");
  88. } else {
  89. loginState.value = false;
  90. }
  91. async function checkCode(method: string = "") {
  92. loading.value = true;
  93. let response: any = await mainStore.qrAddTime(ser_no.value);
  94. loading.value = false;
  95. if (response.status === 200) {
  96. time.value = response?.data.time_added;
  97. loginState.value = true;
  98. setDialog(true);
  99. } else if (response.response.status === 400) {
  100. setDialog(
  101. false,
  102. "此序號已被使用 <br> This serial number is already used"
  103. );
  104. }
  105. }
  106. // if (route.params.ser_no) {
  107. // ser_no.value = route.params.ser_no;
  108. // console.log("ser_no.value", ser_no.value);
  109. // }
  110. });
  111. const callback: CallbackTypes.CredentialCallback = async (response: any) => {
  112. loading.value = true;
  113. const userData: any = decodeCredential(response.credential);
  114. let res = await mainStore.qrGoogleLogin(userData.email, ser_no.value);
  115. loading.value = false;
  116. if (res?.data.time_added === -1) {
  117. setDialog(false, "此序號無效");
  118. } else if (res?.status === 200) {
  119. setTimeout(() => {
  120. setDialog(true);
  121. }, 500);
  122. }
  123. };
  124. </script>
  125. <template>
  126. <!-- <Navbar /> -->
  127. <v-container fluid class="pa-0 overflow-hidden">
  128. <div class="ai_anchor3_content">
  129. <div class="ai_anchor3_content_box">
  130. <img
  131. class="ai_anchor3_content_title img-fluid"
  132. src="../assets/img/qrcode/aianchor3-title.png"
  133. alt=""
  134. />
  135. <h1>集仕多股份有限公司</h1>
  136. <h1>AI 主播&ensp;儲值禮物卡</h1>
  137. </div>
  138. <div class="ai_anchor3_content_start">
  139. <!-- <h1>開放倒數</h1> -->
  140. <img
  141. class="img-fluid"
  142. src="../assets/img/qrcode/startline.png"
  143. alt=""
  144. />
  145. </div>
  146. <v-row
  147. align="center"
  148. justify="center"
  149. no-gutters
  150. class="overflow-hidden mx-auto login-form"
  151. v-if="!loginState"
  152. >
  153. <v-col cols="12" class="px-6 my-8 my-md-0">
  154. <div class="form-title">
  155. <h3>登入後即可獲得儲值金</h3>
  156. <span></span>
  157. </div>
  158. <v-form ref="form" lazy-validation>
  159. <v-text-field
  160. v-model="email"
  161. name="email"
  162. prepend-icon="person"
  163. :rules="[(v) => !!v || '請輸入您的帳號']"
  164. :label="$t('emailAddress')"
  165. required
  166. >
  167. </v-text-field>
  168. <v-text-field
  169. v-model="password"
  170. name="password"
  171. id="password"
  172. prepend-icon="key"
  173. :append-icon="showPassword ? 'visibility' : 'visibility_off'"
  174. :rules="[(v) => !!v || '請輸入您的密碼']"
  175. :type="showPassword ? 'text' : 'password'"
  176. :label="$t('password')"
  177. hint="4-12 位數密碼"
  178. @click:append="showPassword = !showPassword"
  179. @keyup.enter="submit"
  180. required
  181. >
  182. </v-text-field>
  183. <!-- <p class="text-center">
  184. {{ t("haventAccount") }}
  185. <router-link to="/signup">{{ t("register") }}</router-link> /
  186. <router-link to="/recover-password">{{
  187. t("forgotPsd")
  188. }}</router-link>
  189. </p> -->
  190. <div class="d-flex flex-column">
  191. <v-btn rounded="pill" @click.prevent="submit" class="login-btn">
  192. {{ t("loginLink") }}
  193. </v-btn>
  194. <section class="line">
  195. <p class="d-none d-sm-block">
  196. 沒有帳號嗎?使用 Google 快速註冊
  197. </p>
  198. <p class="d-block d-sm-none">
  199. 沒有帳號嗎? <br />
  200. 使用 Google 快速註冊
  201. </p>
  202. <!-- <span></span> -->
  203. </section>
  204. <div class="mx-auto mt-5" style="max-width: 235px">
  205. <GoogleLogin
  206. :callback="callback"
  207. prompt
  208. popup-type="TOKEN"
  209. class="w-100"
  210. />
  211. </div>
  212. </div>
  213. </v-form>
  214. </v-col>
  215. </v-row>
  216. <div class="ai_anchor_moichiu left-70" style="background: #67b5b5">
  217. <v-row align="center" no-gutters class="px-0 mx-0">
  218. <v-col cols="12" sm="4">
  219. <div class="line1">
  220. <img
  221. class="img-fluid anchor_moichiu"
  222. src="../assets/img/qrcode/moichiu.webp"
  223. alt=""
  224. />
  225. <img
  226. class="ai_anchor_line1"
  227. src="../assets/img/qrcode/line1.png"
  228. alt=""
  229. />
  230. </div>
  231. <div class="anchor_name">
  232. <img
  233. class="img-fluid"
  234. src="../assets/img/qrcode/moichiu.png"
  235. alt=""
  236. />
  237. </div>
  238. </v-col>
  239. <v-col :cols="width">
  240. <div class="ai_anchor_moichiu_text">
  241. <p>To. 親愛的 VIP</p>
  242. <p>我知道您們已經準備好,開始使用 AI 主播的系統了</p>
  243. <p>儲值金還在來的路上</p>
  244. <!-- <p>在此之前,系統目前測試中</p> -->
  245. <p>預計 2023/04/06 後開始正式上線!</p>
  246. </div>
  247. </v-col>
  248. </v-row>
  249. </div>
  250. <!-- <div class="mt-100">
  251. <div class="ai_anchor_moichiu" style="background: #ce96c1">
  252. <v-row align="center" no-gutters class="px-0 mx-0">
  253. <v-col :cols="width">
  254. <div class="ai_anchor_moichiu_text">
  255. <p>如果有任何不清楚的,歡迎與我們聯繫</p>
  256. <p>AI 主播系統裡會有使用教學</p>
  257. <p>現在即可開始使用</p>
  258. <p>先登入系統,看看製作影片還需要哪些東西吧!</p>
  259. <p>感謝您的耐心等候</p>
  260. </div>
  261. </v-col>
  262. <v-col cols="4">
  263. <div class="line2">
  264. <img
  265. class="img-fluid anchor_angela"
  266. src="../assets/img/qrcode/angela.webp"
  267. alt=""
  268. />
  269. <img
  270. class="ai_anchor_line2"
  271. src="../assets/img/qrcode/line2.png"
  272. alt=""
  273. />
  274. </div>
  275. <div class="anchor_name2">
  276. <img
  277. class="img-fluid"
  278. src="../assets/img/qrcode/Angela.png"
  279. alt=""
  280. />
  281. </div>
  282. </v-col>
  283. </v-row>
  284. </div>
  285. </div> -->
  286. <div class="progress-item text-center">
  287. <v-progress-circular
  288. indeterminate
  289. color="primary"
  290. :size="50"
  291. v-if="loading"
  292. ></v-progress-circular>
  293. </div>
  294. <div class="CTA_Button_div text-center">
  295. <button type="button" class="CTA_Button">
  296. <!-- 點我開始製作 AI 主播!<br />Log In -->
  297. AI 三代主播系統 <br />
  298. 於 2023/04/06 正式上線!
  299. </button>
  300. </div>
  301. <div class="CTA_box">
  302. <a href="https://ai.choozmo.com/ai-presenter/info/" target="_blank">
  303. <div class="cta-content">看更多<br />官網介紹</div>
  304. </a>
  305. <a href="https://ai.choozmo.com/contact/service/" target="_blank">
  306. <div class="cta-content">
  307. 聯絡我們
  308. <img class="icon20" src="../assets/img/qrcode/icon-19.png" alt="" />
  309. </div>
  310. </a>
  311. <a href="https://line.me/R/ti/p/@choozmo?from=page" target="_blank">
  312. <div class="cta-content">
  313. 關注我們
  314. <img class="icon20" src="../assets/img/qrcode/icon-20.png" alt="" />
  315. </div>
  316. </a>
  317. </div>
  318. <div class="logo_box">
  319. <img
  320. class="choozmologo"
  321. src="../assets/img/qrcode/choozmologo.png"
  322. alt=""
  323. />
  324. </div>
  325. </div>
  326. <Dialog
  327. :msg="dialog.msg"
  328. :state="dialog.state"
  329. :dialog="dialog.show"
  330. :icon="dialog.icon"
  331. :qrcode="true"
  332. @close="dialog.show = false"
  333. ></Dialog>
  334. </v-container>
  335. </template>
  336. <style lang="scss" scoped>
  337. .ai_anchor3_content {
  338. background-image: url("../assets/img/qrcode/aianchor3bg.png");
  339. width: 100%;
  340. background-size: cover;
  341. background-repeat: no-repeat;
  342. }
  343. .login-form {
  344. color: #fff;
  345. margin-bottom: 100px;
  346. .line {
  347. margin-top: 50px;
  348. display: flex;
  349. justify-content: center;
  350. position: relative;
  351. p {
  352. position: relative;
  353. z-index: 1;
  354. color: #fff;
  355. letter-spacing: 1px;
  356. font-size: 24px;
  357. font-weight: bold;
  358. text-align: center;
  359. @media (max-width: 600px) {
  360. font-size: 18px;
  361. }
  362. &::after,
  363. &::before {
  364. content: "";
  365. display: block;
  366. height: 1px;
  367. width: 50px;
  368. background: #fff;
  369. position: absolute;
  370. bottom: 18px;
  371. @media (max-width: 600px) {
  372. bottom: 20px;
  373. }
  374. }
  375. &::after {
  376. left: -55px;
  377. }
  378. &::before {
  379. right: -55px;
  380. }
  381. }
  382. }
  383. .login-btn {
  384. color: #fff;
  385. background: #4f4fa0;
  386. }
  387. .form-title span {
  388. background: #fff !important;
  389. }
  390. }
  391. .logo_box {
  392. text-align: end;
  393. overflow: hidden;
  394. }
  395. .choozmologo {
  396. width: 500px;
  397. margin-right: -50px;
  398. margin-bottom: -80px;
  399. @media (max-width: 767px) {
  400. width: 80vw;
  401. }
  402. @media (max-width: 376px) {
  403. width: 85vw;
  404. }
  405. }
  406. .cta-content {
  407. width: 200px;
  408. height: 130px;
  409. margin: 0 20px;
  410. display: flex;
  411. flex-direction: column;
  412. justify-content: center;
  413. align-items: center;
  414. border: 3px solid #fff;
  415. padding: 30px 20px;
  416. color: #fff;
  417. border-radius: 10px;
  418. text-align: center;
  419. font-size: 24px;
  420. @media (max-width: 767px) {
  421. width: 100%;
  422. margin: 5vh auto 0;
  423. }
  424. @media (max-width: 376px) {
  425. width: 187px;
  426. }
  427. }
  428. .icon20 {
  429. width: 50px;
  430. }
  431. .CTA_box {
  432. width: 50%;
  433. margin: 100px auto;
  434. display: flex;
  435. justify-content: space-evenly;
  436. @media (max-width: 1200px) {
  437. width: 70%;
  438. }
  439. @media (max-width: 767px) {
  440. flex-direction: column;
  441. }
  442. }
  443. .CTA_Button_div {
  444. margin-top: 100px;
  445. }
  446. .CTA_Button {
  447. width: 400px;
  448. height: 120px;
  449. border-radius: 50px;
  450. background: #4f4fa0;
  451. transition: all 300ms ease-in-out;
  452. box-shadow: 1px 15px #2e3287;
  453. color: #fff;
  454. border: none;
  455. padding: 0px 30px;
  456. font-size: 24px;
  457. position: relative;
  458. bottom: 0px;
  459. left: 0px;
  460. transition: all 300ms ease-in-out;
  461. font-weight: 600;
  462. letter-spacing: 2px;
  463. &:hover {
  464. bottom: -10px;
  465. box-shadow: none;
  466. }
  467. @media (max-width: 991px) {
  468. width: 80%;
  469. }
  470. @media (max-width: 767px) {
  471. width: 80%;
  472. margin-top: 20vh;
  473. }
  474. @media (max-width: 575px) {
  475. margin-top: 25vh;
  476. font-size: 20px;
  477. }
  478. }
  479. .ai_anchor3_content_box {
  480. width: 50vw;
  481. margin: 0 auto;
  482. padding-top: 150px;
  483. text-align: center;
  484. color: #fff;
  485. h1 {
  486. font-size: 2rem;
  487. }
  488. @media (max-width: 991px) {
  489. width: 80vw;
  490. }
  491. @media (max-width: 767px) {
  492. width: 90vw;
  493. }
  494. }
  495. .ai_anchor_moichiu_text {
  496. @media (max-width: 767px) {
  497. height: 300px;
  498. padding: 0;
  499. display: flex;
  500. flex-direction: column;
  501. justify-content: center;
  502. }
  503. }
  504. .ai_anchor3_content_start {
  505. padding-top: 50px;
  506. width: 93%;
  507. margin: 0 auto;
  508. text-align: center;
  509. color: #fff;
  510. margin-bottom: 15px;
  511. h1 {
  512. margin-bottom: -60px;
  513. }
  514. }
  515. .left-70 {
  516. left: 70px;
  517. @media (max-width: 991px) {
  518. left: 0px;
  519. }
  520. }
  521. .mt-100 {
  522. margin-top: 100px;
  523. @media (max-width: 767px) {
  524. margin-top: 25vh;
  525. }
  526. @media (max-width: 400px) {
  527. margin-top: 35vh;
  528. }
  529. }
  530. .ai_anchor_moichiu {
  531. width: 75%;
  532. margin: auto;
  533. border-radius: 1rem;
  534. padding: 50px 10px;
  535. position: relative;
  536. @media (max-width: 991px) {
  537. width: 80%;
  538. padding: 40px 10px;
  539. }
  540. @media (max-width: 767px) {
  541. padding: 20px 10px;
  542. }
  543. @media (max-width: 767px) {
  544. width: 90%;
  545. }
  546. .anchor_moichiu {
  547. margin-left: -60px;
  548. @media (max-width: 767px) {
  549. margin-bottom: 0px;
  550. margin-top: 0;
  551. position: absolute;
  552. width: 50vw;
  553. left: 53px;
  554. bottom: -134px;
  555. }
  556. }
  557. .anchor_moichiu,
  558. .anchor_angela {
  559. width: 350px;
  560. max-width: unset;
  561. @media (max-width: 1200px) {
  562. width: 300px;
  563. }
  564. @media (max-width: 991px) {
  565. width: 275px;
  566. }
  567. }
  568. .anchor_angela {
  569. @media (max-width: 767px) {
  570. margin-bottom: 0px;
  571. margin-top: 0;
  572. position: absolute;
  573. right: -60vw;
  574. bottom: -103px;
  575. }
  576. }
  577. .anchor_name {
  578. position: absolute;
  579. bottom: -20px;
  580. left: -8px;
  581. width: 80px;
  582. @media (max-width: 991px) {
  583. width: 50px;
  584. left: -8px;
  585. bottom: -20px;
  586. }
  587. @media (max-width: 767px) {
  588. left: 250px;
  589. bottom: -170px;
  590. }
  591. img {
  592. @media (max-width: 1200px) {
  593. max-width: unset;
  594. width: 70px;
  595. }
  596. @media (max-width: 991px) {
  597. max-width: unset;
  598. width: 65px;
  599. }
  600. }
  601. }
  602. .anchor_name2 {
  603. position: absolute;
  604. bottom: -5px;
  605. right: 0;
  606. width: 100px;
  607. @media (max-width: 991px) {
  608. bottom: -8px;
  609. right: -3px;
  610. width: 80px;
  611. }
  612. @media (max-width: 767px) {
  613. bottom: -27vw;
  614. right: 280px;
  615. width: 100px;
  616. }
  617. @media (max-width: 575px) {
  618. bottom: -32vw;
  619. right: 270px;
  620. width: 70px;
  621. }
  622. @media (max-width: 414px) {
  623. bottom: -40vw;
  624. right: 285px;
  625. }
  626. p {
  627. color: #fff !important;
  628. font-size: 14px;
  629. margin-bottom: 0rem;
  630. }
  631. img {
  632. width: 100px;
  633. @media (max-width: 991px) {
  634. width: 80px;
  635. }
  636. @media (max-width: 767px) {
  637. width: 100px;
  638. }
  639. }
  640. }
  641. .line1,
  642. .line2 {
  643. position: relative;
  644. }
  645. .line1 {
  646. @media (max-width: 767px) {
  647. bottom: -360px;
  648. }
  649. }
  650. .line2 {
  651. @media (max-width: 991px) {
  652. margin-left: 30px;
  653. }
  654. @media (max-width: 767px) {
  655. bottom: -120px;
  656. }
  657. }
  658. .ai_anchor_line1 {
  659. max-width: 1500px;
  660. position: absolute;
  661. bottom: 0px;
  662. left: 165px;
  663. object-fit: cover;
  664. @media (max-width: 1200px) {
  665. max-width: 1000px;
  666. left: 140px;
  667. }
  668. @media (max-width: 991px) {
  669. width: 85vw;
  670. left: 123px;
  671. }
  672. @media (max-width: 767px) {
  673. left: 175px;
  674. bottom: -138px;
  675. }
  676. @media (max-width: 575px) {
  677. left: 168px;
  678. }
  679. }
  680. .ai_anchor_line2 {
  681. width: 1300px;
  682. height: 290px;
  683. position: absolute;
  684. bottom: 6px;
  685. left: -1238px;
  686. @media (max-width: 1200px) {
  687. width: 830px;
  688. height: auto;
  689. bottom: -5px;
  690. left: -775px;
  691. }
  692. @media (max-width: 991px) {
  693. width: 700px;
  694. height: auto;
  695. bottom: -5px;
  696. left: -665px;
  697. }
  698. @media (max-width: 767px) {
  699. width: 100vw;
  700. bottom: -15vw;
  701. left: -345px;
  702. }
  703. @media (max-width: 575px) {
  704. left: -333px;
  705. }
  706. }
  707. p {
  708. font-weight: 900;
  709. letter-spacing: 3px;
  710. text-align: center;
  711. margin-bottom: 0.3rem;
  712. font-size: 22px;
  713. @media (max-width: 991px) {
  714. font-size: 20px;
  715. }
  716. @media (max-width: 767px) {
  717. font-size: 16px;
  718. }
  719. }
  720. }
  721. .img-fluid {
  722. max-width: 100%;
  723. height: auto;
  724. }
  725. .progress-item {
  726. position: fixed;
  727. top: 50%;
  728. left: 50%;
  729. transform: translate(-50%, -50%);
  730. }
  731. </style>