Kaynağa Gözat

feat: 下载的模块中的登陆模块已完成

chenpeng 3 hafta önce
ebeveyn
işleme
5ec142f8e6

+ 3 - 0
app.vue

@@ -3,8 +3,10 @@
 <script setup>
 import '@unocss/reset/tailwind.css'
 import LoginModal from '~/components/common/login/index.vue'
+import loginAndDownloadModal from '~/components/common/loginAndDownload/index.vue'
 
 const { isLoginModalOpen } = useLoginModal()
+const { isLoginAndDownloadOpen } = useLoginAndDownLoadModal()
 
 const router = useRouter()
 router.beforeEach(() => {
@@ -63,6 +65,7 @@ useHead({
   <NuxtLayout>
     <NuxtPage />
     <LoginModal v-if="isLoginModalOpen" />
+    <loginAndDownloadModal v-if="isLoginAndDownloadOpen" />
   </NuxtLayout>
 </template>
 

+ 1 - 1
components/business/categories/comp/item.vue

@@ -17,7 +17,7 @@ defineProps({
         <div class="text-16px text-#999 lh-22px line-clamp-2 mb-20px">
           {{ item.description }}
         </div>
-        <el-button class="!bg-#9B6CFF !text-#fff !b-#9B6CFF !h-40px px-24px">
+        <el-button class="!bg-#9B6CFF !text-#fff !b-#9B6CFF !h-40px px-24px" @click="$emit('select', item)">
           Download
           <svgo-arrow-line-r class="w-16px h-16px ml-10px !text-#fff" />
         </el-button>

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

@@ -0,0 +1,50 @@
+<script lang='ts' setup>
+import useLogin from '../../useLoginAndDownload'
+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/loginAndDownload/comp/choice.vue

@@ -0,0 +1,32 @@
+<script lang='ts' setup>
+import useLogin from '../useLoginAndDownload'
+
+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/loginAndDownload/comp/email/code.vue

@@ -0,0 +1,62 @@
+<script lang='ts' setup>
+import type { FormInstance, FormRules } from 'element-plus'
+import useLogin from '../../useLoginAndDownload'
+
+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/loginAndDownload/comp/email/google.vue

@@ -0,0 +1,45 @@
+<script lang='ts' setup>
+import type { FormInstance, FormRules } from 'element-plus'
+import useLogin from '../../useLoginAndDownload'
+
+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/loginAndDownload/comp/email/index.vue

@@ -0,0 +1,47 @@
+<script lang='ts' setup>
+import type { FormInstance, FormRules } from 'element-plus'
+import useLogin from '../../useLoginAndDownload'
+
+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>

+ 100 - 0
components/common/loginAndDownload/index.vue

@@ -0,0 +1,100 @@
+<!-- @format -->
+
+<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 useLoginAndDownload from './useLoginAndDownload'
+import { useUserStore } from '@/stores/modules/user'
+import { useCommonStore } from '@/stores/modules/common'
+
+const commonStore = useCommonStore()
+const { downloadCatalog, accountList, loginType } = storeToRefs(commonStore)
+const { emailStep, isEmailGoogle, selectedAccount } = useLoginAndDownload()
+
+const userStore = useUserStore()
+const { isLogin } = storeToRefs(userStore)
+
+const { isLoginAndDownloadOpen } = useLoginAndDownLoadModal()
+
+watch(() => accountList.value, (newVal: any) => {
+  if (newVal.length > 0)
+    commonStore.setLoginType('account')
+  else
+    commonStore.setLoginType('choice')
+}, { immediate: true })
+</script>
+
+<template>
+  <el-dialog
+    v-model="isLoginAndDownloadOpen"
+    :append-to-body="true"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    width="880"
+  >
+    <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="isLoginAndDownloadOpen = 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>
+        <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">
+        <div v-if="!isLogin">
+          <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>
+    </div>
+  </el-dialog>
+</template>
+
+<style lang="less">
+.el-dialog {
+  border-radius: 10px;
+  padding: 0;
+  overflow: hidden;
+  .el-dialog__header {
+    display: none;
+    &.show-close {
+      .el-dialog__headerbtn {
+        width: 60px;
+        height: 60px;
+        .el-dialog__close {
+          font-size: 30px;
+          color: #000;
+        }
+      }
+    }
+  }
+}
+</style>

+ 128 - 0
components/common/loginAndDownload/useLoginAndDownload.ts

@@ -0,0 +1,128 @@
+import type { FormInstance, FormRules } from 'element-plus'
+import { registerApi } from '@/api/model/user'
+import { useUserStore } from '@/stores/modules/user'
+
+const { isLoginAndDownloadOpen } = useLoginAndDownLoadModal()
+
+// const userStore = useUserStore()
+
+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 })
+      isLoginAndDownloadOpen.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 }
+}

+ 37 - 0
composables/useLoginAndDownloadModal.ts

@@ -0,0 +1,37 @@
+import { ref } from 'vue'
+// import { useUserStore } from '@/stores/modules/user'
+
+interface LoginResult {
+  status: boolean
+  error?: string
+  isFirstLogin?: boolean
+}
+const isLoginAndDownloadOpen = ref(false)
+let resolvePromise: ((value: LoginResult) => void) | null = null
+
+export function useLoginAndDownLoadModal() {
+  const openLoginAndDownloadModal = (): Promise<LoginResult> => {
+    // const userStore = useUserStore()
+    // const { isLogin } = storeToRefs(userStore)
+    // if (isLogin.value)
+    //   return Promise.resolve({ status: true, isFirstLogin: false })
+    isLoginAndDownloadOpen.value = true
+    return new Promise((resolve) => {
+      resolvePromise = resolve
+    })
+  }
+
+  const closeLoginAndDownloadModal = (result: LoginResult) => {
+    isLoginAndDownloadOpen.value = false
+    if (resolvePromise) {
+      resolvePromise(result)
+      resolvePromise = null
+    }
+  }
+
+  return {
+    isLoginAndDownloadOpen,
+    openLoginAndDownloadModal,
+    closeLoginAndDownloadModal,
+  }
+}

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

@@ -1,4 +1,7 @@
 <script lang='ts' setup>
+import { useCommonStore } from '@/stores/modules/common'
+
+const commonStore = useCommonStore()
 const list = [
   {
     id: 1,
@@ -25,6 +28,19 @@ const list = [
     img: 'https://picsum.photos/560/310',
   },
 ]
+const { openLoginAndDownloadModal } = useLoginAndDownLoadModal()
+
+async function clickLoginAndDownload(item: any) {
+  try {
+    commonStore.setDownloadCatalog(item)
+    const { status } = await openLoginAndDownloadModal()
+    if (status)
+      location.reload()
+  }
+  catch (error) {
+    console.log(error)
+  }
+}
 </script>
 
 <template>
@@ -45,7 +61,7 @@ const list = [
       </div>
       <div class="grid grid-cols-2 gap-64px text-left">
         <div v-for="item in list" :key="item.id">
-          <business-categories-comp-item :item="item" />
+          <business-categories-comp-item :item="item" @select="clickLoginAndDownload" />
         </div>
       </div>
     </div>

+ 12 - 0
stores/modules/common.ts

@@ -10,6 +10,7 @@ export const useCommonStore = defineStore(
     const navigateTextColor = ref('#ffffff')
     const navigateBgColor = ref('#0F0820')
     const loginType = ref<string>('choice')
+    const downloadCatalog = ref<any>(null)
     const accountList = ref([
       {
         name: 'EJET Spark 12',
@@ -59,14 +60,25 @@ export const useCommonStore = defineStore(
       loginType.value = type
     }
 
+    /**
+     * 设置当前的下载的目录数据
+     */
+    const setDownloadCatalog = (catalog: any) => {
+      if (!catalog)
+        return
+      downloadCatalog.value = catalog
+    }
+
     return {
       navigateTextColor,
       navigateBgColor,
+      downloadCatalog,
       setNavigateBgColor,
       setLoginType,
       accountList,
       loginType,
       setAccountList,
+      setDownloadCatalog,
     }
   },
   {