Quellcode durchsuchen

feat: 左上角直接登陆+字体库

chenpeng vor 3 Wochen
Ursprung
Commit
1356db4555

+ 3 - 1
app.vue

@@ -4,6 +4,8 @@
 import '@unocss/reset/tailwind.css'
 import LoginModal from '~/components/common/login/index.vue'
 
+const { isLoginModalOpen } = useLoginModal()
+
 const router = useRouter()
 router.beforeEach(() => {
   const timer = setTimeout(() => {
@@ -60,7 +62,7 @@ useHead({
 <template>
   <NuxtLayout>
     <NuxtPage />
-    <LoginModal />
+    <LoginModal v-if="isLoginModalOpen" />
   </NuxtLayout>
 </template>
 

BIN
assets/fonts/Nunito_Sans_800.ttf


BIN
assets/images/google_logo.png


BIN
assets/images/login_back_arrow.png


BIN
assets/images/login_close_icon.png


BIN
assets/images/login_icon01.png


BIN
assets/images/login_img01.png


BIN
assets/images/mail_logo.png


+ 41 - 1
assets/style/common.less

@@ -1,6 +1,6 @@
 @font-face {
     font-family: 'CustomTitleFont';
-    src: url("~/assets/fonts/Nunito_Sans.ttf")format('truetype');
+    src: url("~/assets/fonts/Nunito_Sans_800.ttf")format('truetype');
     font-weight: normal;
     font-style: normal;
 }
@@ -186,6 +186,46 @@
     }
 }
 
+.custom-title-bg {
+    position: relative;
+    display: inline-block;
+    background: url(~/assets/images/title_bg.png);
+    background-repeat: no-repeat;
+    background-position: center 100%;
+    background-size: 100% auto;
+    padding-bottom: 8px;
+}
+
+.custom-title-bg02 {
+    position: relative;
+    display: inline-block;
+    background: url(~/assets/images/title_bg02.png);
+    background-repeat: no-repeat;
+    background-position: center 100%;
+    background-size: 100% auto;
+    padding-bottom: 8px;
+}
+
+.custom-title-bg03 {
+    position: relative;
+    display: inline-block;
+    background: url(~/assets/images/title_bg03.png);
+    background-repeat: no-repeat;
+    background-position: center 100%;
+    background-size: 100% auto;
+    padding-bottom: 8px;
+}
+
+.custom-title-bg04 {
+    position: relative;
+    display: inline-block;
+    background: url(~/assets/images/title_bg04.png);
+    background-repeat: no-repeat;
+    background-position: center 100%;
+    background-size: 100% auto;
+    padding-bottom: 8px;
+}
+
 // .el-checkbox {
 //     .el-checkbox__input {
 //         &.is-checked {

+ 2 - 10
components/business/categories/block.vue

@@ -28,7 +28,7 @@ const list = [
   <div class="bg-#F9FAFB py-120px">
     <div class="w-1200-auto text-center">
       <h2 class="text-36px fw-800 text-#333 !mb-80px custom-title-font">
-        EJET Spark Selects Products <span class="custom-title-bg">With</span>
+        EJET Spark Selects Products <span class="custom-title-bg03">With</span>
       </h2>
       <div class="flex gap-40px">
         <div v-for="(item, index) in list" :key="index">
@@ -40,13 +40,5 @@ const list = [
 </template>
 
 <style lang='less' scoped>
-.custom-title-bg {
-  position: relative;
-  display: inline-block;
-  background: url(~/assets/images/title_bg03.png);
-  background-repeat: no-repeat;
-  background-position: center 100%;
-  background-size: 100% auto;
-  padding-bottom: 8px;
-}
+ 
 </style>

+ 1 - 9
components/business/categories/list.vue

@@ -110,13 +110,5 @@ const list_column_three = [
 </template>
 
 <style lang="less" scoped>
-.custom-title-bg {
-  position: relative;
-  display: inline-block;
-  background: url(~/assets/images/title_bg.png);
-  background-repeat: no-repeat;
-  background-position: center 100%;
-  background-size: 100% auto;
-  padding-bottom: 8px;
-}
+ 
 </style>

+ 1 - 9
components/business/home/solutions.vue

@@ -176,13 +176,5 @@ const solutionList = computed(() => {
 </template>
 
 <style lang="less" scoped>
-.custom-title-bg{
-  position: relative;
-  display: inline-block;
-  background: url(~/assets/images/title_bg.png);
-  background-repeat: no-repeat;
-  background-position: center 100%;
-  background-size: 100% auto;
-  padding-bottom: 8px;
-}
+
 </style>

+ 2 - 10
components/business/solutions/block_power.vue

@@ -42,7 +42,7 @@ const diff_data: any = computed(() => {
 <template>
   <div class="w-1200-auto pb-110px">
     <h2 class="text-36px fw-800 text-#333 !mb-88px text-center custom-title-font">
-      Power Your Retail Success with <span class="custom-title-bg"> EJET Spark </span>
+      Power Your Retail Success with <span class="custom-title-bg04"> EJET Spark </span>
     </h2>
     <div class="flex gap-40px">
       <div class="flex flex-col gap-60px">
@@ -80,13 +80,5 @@ const diff_data: any = computed(() => {
 </template>
 
 <style lang='less' scoped>
-.custom-title-bg{
-  position: relative;
-  display: inline-block;
-  background: url(~/assets/images/title_bg04.png);
-  background-repeat: no-repeat;
-  background-position: center 100%;
-  background-size: 100% auto;
-  padding-bottom: 8px;
-}
+
 </style>

+ 1 - 10
components/business/solutions/block_solve.vue

@@ -114,7 +114,7 @@ const solutionList = computed(() => {
     </div>
     <div class="py-120px text-center">
       <h2 class="text-36px fw-800 text-#333 !mb-20px">
-        EJET Spark For <span class="custom-title-bg"> {{ list.find(item => item.key === solution)?.title }} </span>
+        EJET Spark For <span class="custom-title-bg04"> {{ list.find(item => item.key === solution)?.title }} </span>
       </h2>
       <div class="lh-24px text-#999 text-22px mb-80px">
         {{ list.find(item => item.key === solution)?.description }}
@@ -154,13 +154,4 @@ const solutionList = computed(() => {
 </template>
 
 <style lang='less' scoped>
-.custom-title-bg{
-  position: relative;
-  display: inline-block;
-  background: url(~/assets/images/title_bg04.png);
-  background-repeat: no-repeat;
-  background-position: center 100%;
-  background-size: 100% auto;
-  padding-bottom: 8px;
-}
 </style>

+ 1 - 10
components/business/solutions/block_steps.vue

@@ -26,7 +26,7 @@ const steps = [
   <div class="py-120px bg-#fff">
     <div class="w-1200-auto">
       <h2 class="text-36px fw-800 text-#333 !mb-20px text-center custom-title-font">
-        Partner With Us in 3  <span class="custom-title-bg"> Steps </span>
+        Partner With Us in 3  <span class="custom-title-bg03"> Steps </span>
       </h2>
       <div class="flex gap-112px text-center">
         <div v-for="item, index in steps" :key="index" class="custom-step">
@@ -47,15 +47,6 @@ const steps = [
 </template>
 
 <style lang='less' scoped>
-.custom-title-bg{
-  position: relative;
-  display: inline-block;
-  background: url(~/assets/images/title_bg03.png);
-  background-repeat: no-repeat;
-  background-position: center 100%;
-  background-size: 100% auto;
-  padding-bottom: 8px;
-}
 .custom-step {
     position: relative;
     &::after{

+ 0 - 9
components/business/trends/index.vue

@@ -55,13 +55,4 @@ const list = ref([
 </template>
 
 <style lang="less" scoped>
-.custom-title-bg {
-  position: relative;
-  display: inline-block;
-  background: url(~/assets/images/title_bg.png);
-  background-repeat: no-repeat;
-  background-position: center 100%;
-  background-size: 100% auto;
-  padding-bottom: 8px;
-}
 </style>

+ 2 - 10
components/common/block/blog.vue

@@ -25,7 +25,7 @@ getVideoOrBlogsList()
   >
     <div class="w-1200-auto text-center">
       <h2 class="!text-36px !fw-800 text-#333 !mb-44px custom-title-font">
-        EJET Spark <span class="custom-title-bg">Blog</span>
+        EJET Spark <span class="custom-title-bg03">Blog</span>
       </h2>
       <div class="flex items-center justify-end text-#9B6CFF cursor-pointer text-14px fw-bold mb-20px">
         View All
@@ -41,13 +41,5 @@ getVideoOrBlogsList()
 </template>
 
 <style lang='less' scoped>
-.custom-title-bg{
-  position: relative;
-  display: inline-block;
-  background: url(~/assets/images/title_bg03.png);
-  background-repeat: no-repeat;
-  background-position: center 100%;
-  background-size: 100% auto;
-  padding-bottom: 8px;
-}
+ 
 </style>

+ 2 - 10
components/common/block/catalogs.vue

@@ -56,7 +56,7 @@ function onViewAll() {
   >
     <div class="w-1200-auto text-left pos-relative">
       <h2 class="text-36px fw-800 text-#fff !mb-40px custom-title-font">
-        Download EJET Spark  <span class="custom-title-bg">Catalogs</span>
+        Download EJET Spark  <span class="custom-title-bg02">Catalogs</span>
       </h2>
       <div class="flex gap-20px mb-30px text-center">
         <div
@@ -132,15 +132,7 @@ function onViewAll() {
   border-radius: 10px;
   background-color: #fff;
 }
-.custom-title-bg{
-  position: relative;
-  display: inline-block;
-  background: url(~/assets/images/title_bg02.png);
-  background-repeat: no-repeat;
-  background-position: center 100%;
-  background-size: 100% auto;
-  padding-bottom: 8px;
-}
+ 
 .swiper-slide{
     background-color: #F3F4FB;
   &.swiper-slide-prev{

+ 1 - 9
components/common/block/faq.vue

@@ -20,7 +20,7 @@ const activeName = ref()
           Frequently
         </h2>
         <h2 class="!mb-20px text-#333 text-36px !fw-800 custom-title-font">
-          Asked <span class="custom-title-bg">Questions</span>
+          Asked <span class="custom-title-bg04">Questions</span>
         </h2>
         <div
           class="py-14px text-14px w-128px text-center text-#fff b-rd-200px bg-#0F0820 hover:bg-#9B6CFF hover:text-#fff cursor-pointer"
@@ -160,12 +160,4 @@ const activeName = ref()
     }
   }
 }
-.custom-title-bg{
-  position: relative;
-  display: inline-block;
-  background: url(~/assets/images/title_bg04.png);
-  background-repeat: no-repeat;
-  background-position: 88% 150%;
-  background-size: 94% auto;
-}
 </style>

+ 0 - 9
components/common/block/partner.vue

@@ -49,13 +49,4 @@ const colorMode = useColorMode()
 </template>
 
 <style lang='less' scoped>
-.custom-title-bg{
-  position: relative;
-  display: inline-block;
-  background: url(~/assets/images/title_bg.png);
-  background-repeat: no-repeat;
-  background-position: center 100%;
-  background-size: 100% auto;
-  padding-bottom: 8px;
-}
 </style>

+ 50 - 0
components/common/login/comp/account/list.vue

@@ -0,0 +1,50 @@
+<script lang='ts' setup>
+import useLogin from '../../useLogin'
+import { useCommonStore } from '@/stores/modules/common'
+
+const commonStore = useCommonStore()
+const { accountList } = storeToRefs(commonStore)
+const { onSelectAccount } = useLogin()
+function selectOtherLogin() {
+  commonStore.setLoginType('choice')
+}
+</script>
+
+<template>
+  <div>
+    <div class="custom-title-font text-24px fw-800 text-#333 mb-10px">
+      <span class="custom-title-bg03">Sign In</span> to EJET Spark
+    </div>
+    <div class="text-14px text-#1A1A1A lh-22px mb-22px">
+      You alredy have the following EJET Spark accounts, select one to continue.
+    </div>
+
+    <div class="flex flex-col gap-20px">
+      <div v-for="item, index in accountList" :key="index" class="flex py-15px px-35px b-rd-10px b-#D8D8D b-1px b-solid cursor-pointer hover:b-#9B6CFF" @click="onSelectAccount(item)">
+        <!-- 获取首字母 -->
+        <div class="w-50px h-50px bg-#F2E5F2 text-#9B6CFF text-24px flex justify-center b-rd-50% items-center mr-28px">
+          {{ item.name.charAt(0).toUpperCase() }}
+        </div>
+        <div>
+          <div class="text-18px fw-bold text-#333 custom-title-font">
+            {{ item.name }}
+          </div>
+          <div class="text-#9B6CFF mt-8px">
+            {{ item.email }}
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="mt-20px text-#333">
+      Continue with another account. <span class="text-#9B6CFF cursor-pointer hover:underline" @click="selectOtherLogin">Click here⁠.</span>
+    </div>
+  </div>
+</template>
+
+<style lang='less' scoped>
+::v-deep(.login-input) {
+   .el-input__wrapper{
+    border-radius: 10px;
+   }
+}
+</style>

+ 32 - 0
components/common/login/comp/choice.vue

@@ -0,0 +1,32 @@
+<script lang='ts' setup>
+import useLogin from '../useLogin'
+
+const { onSelectEmail } = useLogin()
+</script>
+
+<template>
+  <div>
+    <div class="custom-title-font text-24px fw-800 text-#333 mb-10px">
+      <span class="custom-title-bg03">Sign In</span> to EJET Spark
+    </div>
+    <div class="text-14px text-#1A1A1A lh-22px mb-20px">
+      Use your Email or another service to continue with EJET Spark!
+    </div>
+    <div class="b-rd-200px bg-#fff text-#333 b-1px b-solid b-#D8D8D8 h-48px lh-48px hover:b-#9B6CFF flex justify-center items-center cursor-pointer mb-24px text-14px fw-500">
+      <img src="@/assets/images/google_logo.png" alt="" class="w-24px h-24px mr-10px">
+      Continue with Google
+    </div>
+    <div class="b-rd-200px bg-#fff b-1px text-#333 b-solid b-#D8D8D8 h-48px lh-48px hover:b-#9B6CFF flex justify-center items-center cursor-pointer text-14px fw-500" @click="onSelectEmail">
+      <img src="@/assets/images/mail_logo.png" class="w-24px h-24px mr-10px" alt="">
+      Continue with Email
+    </div>
+
+    <div class="mt-40px lh-22px text-14px text-#767676">
+      By continuing, you agree to EJET Spark’s <span class="text-#1A1A1A underline cursor-pointer fw-500">Terms of Use⁠.</span> Read our
+      <span class="text-#1A1A1A underline cursor-pointer fw-500">Privacy Policy⁠.</span>
+    </div>
+  </div>
+</template>
+
+<style lang='less' scoped>
+</style>

+ 62 - 0
components/common/login/comp/email/code.vue

@@ -0,0 +1,62 @@
+<script lang='ts' setup>
+import type { FormInstance, FormRules } from 'element-plus'
+import useLogin from '../../useLogin'
+
+const { loginForm, finishCode, errorCodeTxt, seconds, getMailCode, backStep } = useLogin()
+const ruleFormRef = ref<FormInstance>()
+
+const rules = ref<FormRules>({
+  code: [
+    { required: true, message: 'Please enter a valid email.', trigger: 'blur' },
+  ],
+})
+</script>
+
+<template>
+  <div>
+    <div class="custom-title-font text-24px fw-800 text-#333 mb-10px flex items-center">
+      <img src="@/assets/images/login_back_arrow.png" alt="" class="w-16px h-16px mr-4px cursor-pointer" @click="backStep">
+      Finish Signing In
+    </div>
+    <div class="text-14px text-#1A1A1A lh-22px mb-20px">
+      Once you enter the code we sent to <span class="text-#1A1A1A fw-700">{{ loginForm.mail }}</span>, you’ll be signed in.
+    </div>
+
+    <div class="my-10px text-14px text-#9B6CFF underline cursor-pointer fw-bold" @click="getMailCode">
+      Get a Code
+    </div>
+    <el-form ref="ruleFormRef" :rules="rules" :model="loginForm">
+      <el-form-item label="" prop="code" class="!mb-24px">
+        <el-input v-model.trim="loginForm.code" :class="!!errorCodeTxt && 'error-txt'" class="!h-46px login-input !text-14px" placeholder="Enter email" />
+        <div v-if="errorCodeTxt" class="text-red mt-10px">
+          {{ errorCodeTxt }}
+        </div>
+      </el-form-item>
+
+      <el-form-item>
+        <el-button class="!w-full !h-50px !text-16px !fw-500 !b-rd-10px !bg-#9B6CFF !text-#fff" @click="finishCode(ruleFormRef)">
+          Continue
+        </el-button>
+      </el-form-item>
+    </el-form>
+    <div class="mt-20px text-14px text-#767676">
+      <div class="mb-8px">
+        Didn't get the code? <span class="text-#9B6CFF cursor-pointer hover:underline" @click="getMailCode">Resend code</span>⁠.
+      </div>
+      <div> Didn't get the code? Resend in {{ seconds }} seconds.</div>
+    </div>
+  </div>
+</template>
+
+<style lang='less' scoped>
+::v-deep(.login-input) {
+  &.error-txt{
+    .el-input__wrapper{
+      border: 1px solid red!important;
+   }
+  }
+   .el-input__wrapper{
+    border-radius: 10px;
+   }
+}
+</style>

+ 45 - 0
components/common/login/comp/email/google.vue

@@ -0,0 +1,45 @@
+<script lang='ts' setup>
+import type { FormInstance, FormRules } from 'element-plus'
+import useLogin from '../../useLogin'
+
+const { loginForm, finishCode, errorCodeTxt, seconds, getMailCode, backStep } = useLogin()
+const ruleFormRef = ref<FormInstance>()
+
+const rules = ref<FormRules>({
+  code: [
+    { required: true, message: 'Please enter a valid email.', trigger: 'blur' },
+  ],
+})
+</script>
+
+<template>
+  <div>
+    <div class="custom-title-font text-24px fw-800 text-#333 mb-10px flex items-center">
+      <img src="@/assets/images/login_back_arrow.png" alt="" class="w-16px h-16px mr-4px cursor-pointer" @click="backStep">
+      Continue with Google
+    </div>
+    <div class="text-14px text-#1A1A1A lh-22px mb-20px">
+      Your Google account is connected to this EJET Spark account. Sign in using the email
+    </div>
+    <div class="my-10px text-14px text-#9B6CFF mb-24px">
+      {{ loginForm.mail }}
+    </div>
+    <div class="b-rd-200px bg-#fff text-#333 b-1px b-solid b-#D8D8D8 h-48px lh-48px hover:b-#9B6CFF flex justify-center items-center cursor-pointer mb-24px text-14px fw-500">
+      <img src="@/assets/images/google_logo.png" alt="" class="w-24px h-24px mr-10px">
+      Continue with Google
+    </div>
+  </div>
+</template>
+
+<style lang='less' scoped>
+::v-deep(.login-input) {
+  &.error-txt{
+    .el-input__wrapper{
+      border: 1px solid red!important;
+   }
+  }
+   .el-input__wrapper{
+    border-radius: 10px;
+   }
+}
+</style>

+ 47 - 0
components/common/login/comp/email/index.vue

@@ -0,0 +1,47 @@
+<script lang='ts' setup>
+import type { FormInstance, FormRules } from 'element-plus'
+import useLogin from '../../useLogin'
+
+const { loginForm, sendEmail, backStep } = useLogin()
+const ruleFormRef = ref<FormInstance>()
+
+const rules = ref<FormRules>({
+  mail: [
+    { required: true, message: 'Please enter a valid email.', trigger: 'blur' },
+    {
+      pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
+      message: 'Please enter a valid email.',
+    },
+  ],
+})
+</script>
+
+<template>
+  <div>
+    <div class="custom-title-font text-24px fw-800 text-#333 mb-10px flex items-center">
+      <img src="@/assets/images/login_back_arrow.png" alt="" class="w-16px h-16px mr-4px cursor-pointer" @click="backStep">
+      Continue with Email
+    </div>
+    <div class="text-14px text-#1A1A1A lh-22px mb-20px">
+      We’ll check if you have an account, and help create one if you don’t.
+    </div>
+    <el-form ref="ruleFormRef" :rules="rules" :model="loginForm">
+      <el-form-item label="" prop="mail" class="!mb-24px">
+        <el-input v-model.trim="loginForm.mail" class="!h-46px login-input !text-14px" placeholder="Enter email" />
+      </el-form-item>
+      <el-form-item>
+        <el-button class="!w-full !h-50px !text-16px !fw-500 !b-rd-10px !bg-#9B6CFF !text-#fff" @click="sendEmail(ruleFormRef)">
+          Continue
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<style lang='less' scoped>
+::v-deep(.login-input) {
+   .el-input__wrapper{
+    border-radius: 10px;
+   }
+}
+</style>

+ 69 - 63
components/common/login/index.vue

@@ -1,88 +1,91 @@
 <script setup lang="ts">
+import Choice from './comp/choice.vue'
+import Mail from './comp/email/index.vue'
+import Code from './comp/email/code.vue'
+import Google from './comp/email/google.vue'
+import AccountList from './comp/account/list.vue'
+import useLogin from './useLogin'
 import { useUserStore } from '@/stores/modules/user'
+import { useCommonStore } from '@/stores/modules/common'
 
+const commonStore = useCommonStore()
+const { accountList, loginType } = storeToRefs(commonStore)
+
+const { emailStep, isEmailGoogle, selectedAccount } = useLogin()
 const { isLoginModalOpen, closeLoginModal } = useLoginModal()
 
-const userStore = useUserStore()
-const { login } = userStore
+// const userStore = useUserStore()
+// const { login } = userStore
 
-const loginForm = ref({
-  email: '',
-  password: '',
-})
-const error = ref(false)
-const isLoading = ref(false)
+// const loginForm = ref({
+//   email: '',
+//   password: '',
+// })
+// const error = ref(false)
+// const isLoading = ref(false)
 
 const route = useRoute()
 
+watch(() => accountList.value, (newVal: any) => {
+  console.log('accountList changed:', newVal)
+  if (newVal.length > 0)
+    commonStore.setLoginType('account')
+  else
+    commonStore.setLoginType('choice')
+}, { immediate: true })
+
 watch(() => route.path, () => {
   if (isLoginModalOpen.value)
     closeLoginModal({ status: false, error: '用户取消登录' })
 })
 
-async function handleLogin() {
-  error.value = false
-  isLoading.value = true
-  try {
-    await login(loginForm.value)
-    closeLoginModal({ status: true, isFirstLogin: true })
-  }
-  catch (err) {
-    error.value = true
-  }
-  finally {
-    isLoading.value = false
-  }
-}
-
-function handleClose() {
-  closeLoginModal({ status: false, error: '用户取消登录' })
-}
+// async function handleLogin() {
+//   error.value = false
+//   isLoading.value = true
+//   try {
+//     await login(loginForm.value)
+//     closeLoginModal({ status: true, isFirstLogin: true })
+//   }
+//   catch (err) {
+//     error.value = true
+//   }
+//   finally {
+//     isLoading.value = false
+//   }
+// }
 </script>
 
 <template>
   <el-dialog
     v-model="isLoginModalOpen"
     :append-to-body="true"
-    width="580"
-    @close="handleClose"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    width="880"
   >
-    <div class="p-24px pt-26px text-center ">
-      <h2 class="fw-500 text-20px text-#333">
-        Shop Quality Wholesale Online
-      </h2>
-      <div class="mt-10px mb-30px text-#666666 text-14px">
-        Slgn in to wholesale from over 2,000+ premium brands!
-      </div>
-      <el-form :model="loginForm">
-        <el-form-item label="" class="!mb-20px">
-          <el-input v-model.trim="loginForm.email" class="!h-50px !text-18px" placeholder="Email" />
-        </el-form-item>
-        <el-form-item label="">
-          <el-input v-model.trim="loginForm.password" :show-password="true" class="!h-50px !text-18px" type="password" placeholder="Password" @press-enter="handleLogin" />
-        </el-form-item>
-
-        <div class="flex justify-between mt-10px mb-30px text-18px">
-          <div v-if="error" class="text-#D2161F text-16px">
-            Username or password is incorrect
-          </div>
-          <div v-else />
-          <div class="hover:underline text-14px">
-            <nuxt-link to="/forgot">
-              Forget Password?
-            </nuxt-link>
-          </div>
+    <img src="@/assets/images/login_close_icon.png" alt="" class="w-32px h-32px bg-#F4F4F4 cursor-pointer p-8px b-rd-50% pos-absolute top-24px right-24px" @click="isLoginModalOpen = false">
+    <div class="flex">
+      <div class="pt-110px pb-45px px-45px pos-relative bg-#D7C4FF w-375px">
+        <img src="@/assets/images/login_icon01.png" alt="" class="w-110px h-110px pos-absolute top-0 left-0">
+        <div class="custom-title-font text-24px fw-800 text-#333">
+          Welcome to EJET Spark!
         </div>
-
-        <el-button :disabled="!(loginForm.email && loginForm.password)" class="!bg-#292F36 !text-#fff !b-#292F36 !w-full !h-50px !text-16px !fw-500 !b-rd-6px" @click="handleLogin">
-          LOG IN
-        </el-button>
-        <el-button class="!bg-#CC9879 !text-#fff !w-full !h-50px !text-16px !fw-500 !b-rd-6px !ml-0 mt-16px">
-          <nuxt-link to="/register">
-            No account? SIGN UP
-          </nuxt-link>
-        </el-button>
-      </el-form>
+        <img src="@/assets/images/login_img01.png" alt="" class="my-20px w-276px h-225px mx-auto">
+        <div class="mb-10px custom-title-font text-18px fw-800 text-#333">
+          EJET Spark Intro
+        </div>
+        <div class="text-14px text-#333 lh-22px">
+          EJET Spark catalog description, spark the trend, ignate sales. EJET Spark catalog description, spark the trend, ignate sales.
+        </div>
+      </div>
+      <div class="flex-1 b-rd-10px bg-#fff px-70px pt-100px">
+        <AccountList v-if="emailStep === 0 && accountList.length && loginType === 'account'" />
+        <Choice v-if="emailStep === 0 && loginType === 'choice'" />
+        <Mail v-if="emailStep === 1 && !selectedAccount" />
+        <Mail v-if="emailStep === 1 && selectedAccount" />
+        <Code v-if="emailStep === 2 && !isEmailGoogle " />
+        <Google v-if="emailStep === 2 && isEmailGoogle === 'google'" />
+      </div>
     </div>
   </el-dialog>
 </template>
@@ -90,7 +93,10 @@ function handleClose() {
 <style lang="less">
  .el-dialog {
     border-radius: 10px;
+    padding: 0;
+    overflow: hidden;
     .el-dialog__header{
+      display: none;
         &.show-close{
             .el-dialog__headerbtn{
                 width: 60px;

+ 129 - 0
components/common/login/useLogin.ts

@@ -0,0 +1,129 @@
+import type { FormInstance, FormRules } from 'element-plus'
+import { registerApi } from '@/api/model/user'
+import { useUserStore } from '@/stores/modules/user'
+
+const { isLoginModalOpen, closeLoginModal } = useLoginModal()
+
+// const userStore = useUserStore()
+// const { login } = userStore
+
+const loginForm = ref<any>({
+  mail: '',
+  code: '',
+})
+const emailStep = ref<number>(0)
+const seconds = ref<number>(0)
+const selectedAccount = ref<string>('')
+const errorCodeTxt = ref<string>('')
+const isEmailGoogle = ref<string>('')
+export default function useRegister() {
+  const nextStep = () => {
+    emailStep.value++
+  }
+  const backStep = () => {
+    if (emailStep.value > 0)
+      emailStep.value--
+  }
+  //   const register = async () => {
+  //     try {
+  //       const form = {
+  //         ...params.value,
+  //         purchaseCategory: params.value.purchaseCategory.join(','),
+  //       }
+  //       await registerApi(form)
+  //       nextStep()
+  //     }
+  //     catch (error) {
+  //       console.log(error)
+  //     }
+  //   }
+
+  /**
+   * 二选一选择邮箱--下一步
+   */
+  const onSelectEmail = () => {
+    selectedAccount.value = ''
+    loginForm.value.mail = ''
+    nextStep()
+  }
+  /**
+   * 选择曾经登陆过的账号--下一步
+   */
+  const onSelectAccount = (item: any) => {
+    selectedAccount.value = item.email
+    loginForm.value.mail = item.email
+    emailStep.value = 1
+  }
+  /**
+   * dao: 倒计时60秒
+   */
+  const countSeconds = () => {
+    seconds.value = 60
+    const timer = setInterval(() => {
+      seconds.value--
+      if (seconds.value === 0)
+        clearInterval(timer)
+    }, 1000)
+  }
+  /**
+   * 邮箱--发送验证码
+   */
+  const getMailCode = async () => {
+    try {
+      if (seconds.value > 0)
+        return
+      // 获取验证码
+      //   const res = await registerApi(loginForm.value.mail)
+      countSeconds()
+    }
+    catch (error) {
+      console.error('Error sending email code:', error)
+    }
+  }
+  /**
+   * 邮箱--验证是否存在--是否与已存在google的邮箱--下一步
+   * @returns
+   */
+  async function sendEmail(formEl: FormInstance | undefined) {
+    if (!formEl)
+      return
+    await formEl.validate(async (valid, fields) => {
+      if (valid) {
+      // 验证已存在google的邮箱
+
+        isEmailGoogle.value = 'google' // 模拟google邮箱验证
+        nextStep()
+      }
+      else { console.log('error submit!', fields) }
+    })
+  }
+  /**
+   * 邮箱--验证码--完成注册
+   * @returns
+   */
+  async function finishCode(formEl: FormInstance | undefined) {
+    errorCodeTxt.value = ''
+    if (!formEl)
+      return
+    await formEl.validate(async (valid, fields) => {
+      if (valid)
+        await handleLogin()
+
+      else console.log('error submit!', fields)
+    })
+  }
+  /**
+   * 登录--提交
+   */
+  async function handleLogin() {
+    try {
+    //   await login(loginForm.value)
+    //   closeLoginModal({ status: true, isFirstLogin: true })
+      isLoginModalOpen.value = false
+    }
+    catch (err) {
+      errorCodeTxt.value = 'The code is wrong or invalid.'
+    }
+  }
+  return { emailStep, loginForm, selectedAccount, onSelectAccount, errorCodeTxt, seconds, isEmailGoogle, sendEmail, nextStep, onSelectEmail, finishCode, backStep, getMailCode }
+}

+ 1 - 9
pages/blog/index.vue

@@ -76,7 +76,7 @@ const validateEmail = computed(() => {
   <div>
     <div class="pt-184px pb-50px bg-#F3F4FB text-center">
       <h2 class="text-60px fw-800 ls-2 text-#333 inline-block pos-relative custom-title-font">
-        EJET Spark  <span class="custom-title-bg">Blog</span>
+        EJET Spark  <span class="custom-title-bg03">Blog</span>
         <img src="@/assets/images/blog_icon05.png" class="w-90px h-90px pos-absolute top--10px left--120px" alt="" srcset="">
         <img src="@/assets/images/blog_icon06.png" class="w-70px h-70px pos-absolute top--20px right--100px" alt="" srcset="">
       </h2>
@@ -121,14 +121,6 @@ const validateEmail = computed(() => {
 </template>
 
 <style lang='less' scoped>
-.custom-title-bg{
-  position: relative;
-  display: inline-block;
-  background: url(~/assets/images/title_bg03.png);
-  background-repeat: no-repeat;
-  background-position: 88% 150%;
-  background-size: 94% auto;
-}
 ::v-deep(.custom-input){
   .el-input__wrapper{
     border-radius:4px!important;

+ 1 - 9
pages/categories/[slug].vue

@@ -38,7 +38,7 @@ const list = [
     </div>
     <div class="py-120px w-1200-auto text-center">
       <h2 class="text-36px fw-800 text-#333 !mb-20px custom-title-font">
-        Our Latest Product <span class="custom-title-bg">Catalogs</span>
+        Our Latest Product <span class="custom-title-bg04">Catalogs</span>
       </h2>
       <div class="text-#999 text-22px mb-40px">
         Discover bestsellers and fresh arrivals tailored to your niche.
@@ -59,12 +59,4 @@ const list = [
 .header{
   background-position: 50% 75%;
 }
-.custom-title-bg{
-  position: relative;
-  display: inline-block;
-  background: url(~/assets/images/title_bg04.png);
-  background-repeat: no-repeat;
-  background-position: 88% 150%;
-  background-size: 94% auto;
-}
 </style>

+ 35 - 0
stores/modules/common.ts

@@ -1,6 +1,7 @@
 /** @format */
 
 import { defineStore } from 'pinia'
+import { id } from 'element-plus/es/locales.mjs'
 import { useUserStore } from './user'
 
 export const useCommonStore = defineStore(
@@ -8,8 +9,25 @@ export const useCommonStore = defineStore(
   () => {
     const navigateTextColor = ref('#ffffff')
     const navigateBgColor = ref('#0F0820')
+    const loginType = ref<string>('choice')
+    const accountList = ref([
+      {
+        name: 'EJET Spark 12',
+        email: '185@163.com',
+        id: '1',
+      },
+      {
+        name: 'EJET Spark 34',
+        email: '18525917172@gmail.com',
+        id: '2',
+      },
+    ])
     // const userStore = useUserStore()
 
+    /**
+     * 设置头部导航背景色
+     * @returns
+     */
     const setNavigateBgColor = (color: string) => {
       if (!color)
         return
@@ -27,11 +45,28 @@ export const useCommonStore = defineStore(
         navigateBgColor.value = 'rgba(255, 255, 255, 0.7)'
       }
     }
+    /**
+     * 设置已经登陆的账号列表 (在登陆成功后调用)
+     */
+    const setAccountList = (list: any[]) => {
+      if (!list || !Array.isArray(list))
+        return
+      accountList.value = list
+    }
+    const setLoginType = (type: string) => {
+      if (!type)
+        return
+      loginType.value = type
+    }
 
     return {
       navigateTextColor,
       navigateBgColor,
       setNavigateBgColor,
+      setLoginType,
+      accountList,
+      loginType,
+      setAccountList,
     }
   },
   {