123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- <script setup>
- import { ref, reactive, onMounted, watch } from "vue";
- import { useMainStore } from "@/stores/store";
- import axios from "axios";
- const store = useMainStore();
- const emit = defineEmits(["close"]);
- const emailRegex = /^(?:\s*|[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/;
- let tab = ref("");
- function close() {
- emit("close", false);
- }
- // Google Login
- const handleSignIn = async (response) => {
- const credential = response.credential;
- const profile = JSON.parse(
- decodeURIComponent(
- escape(
- window.atob(
- credential.split(".")[1].replace(/-/g, "+").replace(/_/g, "/")
- )
- )
- )
- );
- console.log("profile", profile);
- if (profile) {
- let data = {
- username: profile.name,
- password: "googleLogin",
- email: profile.email,
- };
- const formData = new FormData();
- for (const key in data) {
- formData.append(key, data[key]);
- }
- formData.forEach((value, key) => {
- console.log(`${key}: ${value}`);
- });
- try {
- const response = await axios.post(
- "https://cmm.ai:8088/api/login/google/access-token",
- formData
- );
- console.log("response", response);
- console.log("response access_token", response.data.access_token);
- if (response.data.access_token) {
- alertText.value = "登入成功";
- loginAlert.value = true;
- isLoading.value = false;
- alertClose(2000);
- setTimeout(() => {
- localStorage.setItem("token", response.data.access_token);
- store.loginState = true;
- }, 1500);
- }
- } catch (error) {
- console.log("google login error");
- console.log(error);
- }
- }
- };
- let isLoading = ref(false);
- let loginAlert = ref(false);
- let userLogin = reactive({
- username: "",
- password: "",
- });
- // 登入
- async function login() {
- if (!emailRegex.test(userLogin.username)) {
- alertText.value = "請檢查 E-mail 格式";
- loginAlert.value = true;
- alertClose(1500);
- return;
- }
- const formData = new FormData();
- for (const key in userLogin) {
- formData.append(key, userLogin[key]);
- }
- try {
- isLoading.value = true;
- const response = await axios.post(
- "https://cmm.ai:8088/api/login",
- formData
- );
- console.log("response", response);
- if (response.data.msg) {
- alertText.value = response.data.msg;
- loginAlert.value = true;
- isLoading.value = false;
- alertClose(2000);
- }
- setTimeout(() => {
- localStorage.setItem("token", response.data.access_token);
- store.loginState = true;
- }, 1500);
- } catch (error) {
- console.error(error);
- }
- }
- let userRegister = reactive({
- email: "",
- username: "",
- password: "",
- re_password: "",
- });
- // 註冊
- async function register() {
- const formData = new FormData();
- for (const key in userRegister) {
- formData.append(key, userRegister[key]);
- }
- try {
- isLoading.value = true;
- const response = await axios.post("https://cmm.ai:8088/api/add", formData);
- console.log("response", response);
- if (response.data.msg) {
- alertText.value = response.data.msg;
- registerAlert.value = true;
- alertClose(2000);
- }
- isLoading.value = false;
- if (response.data.code === 200) {
- setTimeout(() => {
- tab.value = 1;
- }, 3000);
- }
- } catch (error) {
- console.error(error);
- }
- }
- // 檢查欄位是否為空
- function checkEmptyFields() {
- for (const key in userRegister) {
- if (userRegister[key] === "") {
- return false;
- }
- }
- return true;
- }
- let registerAlert = ref(false);
- let alertText = ref("");
- // 檢查註冊欄位
- function validateRegister() {
- const isValid = checkEmptyFields();
- if (!isValid) {
- return;
- }
- if (!emailRegex.test(userRegister.email)) {
- alertText.value = "請檢查 E-mail 格式";
- registerAlert.value = true;
- alertClose(1500);
- return;
- }
- if (userRegister.password !== userRegister.re_password) {
- alertText.value = "密碼與確認密碼不相符";
- registerAlert.value = true;
- alertClose(1500);
- return;
- }
- register();
- }
- function alertClose(second) {
- setTimeout(() => {
- loginAlert.value = false;
- registerAlert.value = false;
- }, second);
- }
- onMounted(() => {
- const script = document.createElement("script");
- script.src = "https://accounts.google.com/gsi/client";
- script.async = true;
- script.defer = true;
- document.head.appendChild(script);
- window.onSignIn1 = handleSignIn;
- });
- let token =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIiLCJleHAiOjE2ODg2MjU5MzN9.rMJGZxHeKaxBQx0VuTBk8AtYhhLyIPxNUHBrsKWXJjE";
- // 解碼 JWT
- const base64Url = token.split(".")[1]; // 取得 JWT 的 payload
- const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/"); // 處理 URL 安全的 Base64 字串
- const decodedPayload = atob(base64); // Base64 解碼
- // 將解碼後的 payload 轉換為 JSON 物件
- const payload = JSON.parse(decodedPayload);
- // 獲取過期時間(exp)
- const expirationTime = payload.exp; // 過期時間的 UNIX 時間戳
- // 創建 Date 物件並設定時間戳
- const expirationDate = new Date(expirationTime * 1000); // JS 時間戳以毫秒為單位故乘以 1000
- // 取得日期和時間的字串表示
- const formattedExpiration = expirationDate.toLocaleString();
- console.log("JWT 過期時間:", formattedExpiration);
- let showLogin = ref(false);
- function toggleLoginInput() {
- console.log("showLogin");
- showLogin.value = !showLogin.value;
- }
- </script>
- <template>
- <!-- <Navbar />
- <div>Login</div> -->
- <!-- <v-container class="pa-0">
- <v-row>
- <v-col cols="6">
- <img src="@/assets/img/img-08.jpg" alt="">
- </v-col>
- <v-col cols="6"></v-col>
- </v-row>
- </v-container> -->
- <v-card class="login-card">
- <div class="d-flex justify-end pa-3">
- <v-icon
- size="large"
- icon="mdi-close"
- color="gray"
- @click.prevent="close()"
- ></v-icon>
- </div>
- <v-tabs v-model="tab" color="primary" align-tabs="center">
- <v-tab :value="1" class="me-5">登入</v-tab>
- <v-tab :value="2">註冊</v-tab>
- </v-tabs>
- <v-window v-model="tab">
- <v-window-item :value="1">
- <div class="px-15 pt-10 d-flex flex-column align-center">
- <div
- id="g_id_onload"
- data-client_id="1019468024352-ktbehd28aia7gjcl53sgaid1ucqb8gcd.apps.googleusercontent.com"
- data-callback="onSignIn1"
- ></div>
- <div
- class="g_id_signin"
- data-type="standard"
- data-theme="outline"
- data-size="large"
- data-logo_alignment="left"
- ></div>
- <!-- <p class="text-center my-7">使用平台帳號登入</p> -->
- <v-btn
- @click="toggleLoginInput()"
- class="login-btn"
- variant="outlined"
- color="primary"
- >
- 使用平台帳號登入
- </v-btn>
- <Transition>
- <v-form v-if="showLogin" @submit.prevent class="mt-7">
- <v-text-field
- v-model="userLogin.username"
- label="信箱"
- :rules="[(v) => !!v || '請輸入您的信箱']"
- prepend-inner-icon="mdi-account"
- variant="solo"
- density="compact"
- ></v-text-field>
- <v-text-field
- v-model="userLogin.password"
- label="密碼"
- :rules="[(v) => !!v || '請輸入您的密碼']"
- prepend-inner-icon="mdi-lock"
- variant="solo"
- density="compact"
- type="password"
- ></v-text-field>
- <v-btn
- type="submit"
- size="large"
- color="primary"
- block
- @click="login()"
- :loading="isLoading"
- >登入</v-btn
- >
- </v-form>
- </Transition>
-
- <div class="alert" v-if="loginAlert">
- <v-alert border="start" border-color="primary" elevation="2">
- {{ alertText }}
- </v-alert>
- </div>
- </div>
- </v-window-item>
- <v-window-item :value="2">
- <div class="d-flex flex-column align-center px-15 pt-10">
- <v-form @submit.prevent>
- <v-select
- label="身份"
- :items="['學員', '開課工藝家', '其他']"
- variant="solo"
- density="compact"
- ></v-select>
- <v-text-field
- v-model="userRegister.email"
- label="信箱"
- :rules="[(v) => !!v || '請輸入您的信箱']"
- prepend-inner-icon="mdi-email"
- variant="solo"
- density="compact"
- ></v-text-field>
- <v-text-field
- v-model="userRegister.username"
- label="帳號"
- :rules="[(v) => !!v || '請輸入您的帳號']"
- prepend-inner-icon="mdi-account"
- variant="solo"
- density="compact"
- ></v-text-field>
- <v-text-field
- v-model="userRegister.password"
- label="密碼"
- :rules="[(v) => !!v || '請輸入您的密碼']"
- prepend-inner-icon="mdi-lock"
- variant="solo"
- density="compact"
- type="password"
- ></v-text-field>
- <v-text-field
- v-model="userRegister.re_password"
- label="確認密碼"
- :rules="[(v) => !!v || '請輸入確認密碼']"
- prepend-inner-icon="mdi-lock-check"
- variant="solo"
- density="compact"
- type="password"
- ></v-text-field>
- <v-btn
- block
- type="submit"
- size="large"
- color="primary"
- @click="validateRegister()"
- :loading="isLoading"
- >註冊</v-btn
- >
- </v-form>
- <div class="alert" v-if="registerAlert">
- <v-alert border="start" border-color="primary" elevation="2">
- {{ alertText }}
- </v-alert>
- </div>
- </div>
- </v-window-item>
- </v-window>
- <v-card-actions class="d-flex justify-center py-10 forget">
- <a href="">忘記密碼</a><span class="mx-1">/</span><a href="">忘記帳號</a>
- </v-card-actions>
- </v-card>
- </template>
- <style lang="scss">
- .login-btn {
- width: 190px;
- margin-top: 20px;
- }
- .login-card {
- .v-tab {
- padding-bottom: 20px;
- font-size: 28px;
- }
- .v-form {
- width: 100%;
- }
- .tab {
- button {
- margin: 0 20px;
- padding-bottom: 20px;
- font-size: 28px;
- font-weight: 500;
- text-align: center;
- color: #d9d9d9;
- border-bottom: 3px solid transparent;
- transition: all 0.3s;
- &.active {
- color: var(--main-color);
- border-bottom: 3px solid var(--main-color);
- }
- }
- }
- h3,
- p {
- color: #595959;
- }
- .google-btn {
- width: 130px;
- margin: auto;
- }
- .v-text-field .v-input__details {
- position: relative;
- top: -4px;
- }
- .forget {
- a,
- span {
- color: #969696;
- }
- a {
- transition: all 0.3s;
- &:hover {
- opacity: 0.8;
- }
- }
- }
- .alert {
- top: 50%;
- position: absolute;
- z-index: 1;
- .v-alert {
- color: var(--main-color);
- background-color: #fff;
- letter-spacing: 1px;
- }
- }
- }
- </style>
|