36 手机号登陆业务逻辑开发 - udo-bit/naive_admin_pro GitHub Wiki

目标

完成手机号登录业务逻辑开发

开发

根据我们上一节课的经验,这节课我们来开发一些手机登录的业务逻辑部分,我们在在login/composables下创建一个mobile-login.ts的文件。代码逻辑同我们的登录部分的逻辑,如下:

import type { FormInst, FormRules } from 'naive-ui'
import type { UserMobileLoginParams } from '~/api/user'
import router from '~/routes'

export const useMobileLogin = () => {
  const mLoading = ref(false)
  const mFormRef = ref<FormInst>()
  const { t } = useI18n()
  const userStore = useUserStore()
  const mModel = reactive<UserMobileLoginParams>({
    mobile: null,
    code: null,
    type: 'mobile',
  })
  const mRules = reactive<FormRules>({
    mobile: [
       {
        key: 'mobile',
        required: true,
        renderMessage: () => t('login.mobile.required'),
      },
      {
        key: 'mobile',
        pattern: /^1[3456789]\d{9}$/,
        renderMessage: () => t('login.mobile.rule'),
      },
    ],
    code: [
      {
        required: true,
        renderMessage: () => t('login.mobile.verification-code.required'),
      },
      {
        min: 6,
        max: 6,
        renderMessage: () => t('login.mobile.verification-code.rule'),
      },
    ],
  })
  const mLogin = async () => {
    mLoading.value = true
    try {
      await mFormRef.value?.validate()
      await userStore.login(mModel)
      mLoading.value = false
      const redirect = router.currentRoute.value?.params?.redirect as string
      await router.replace(redirect || '/')
    }
    catch (e) {
      mLoading.value = false
    }
  }

  return {
    mLoading,
    mFormRef,
    mRules,
    mModel,
    mLogin,
  }
}

下面我们来实现一下获取验证码的部分,首先我们需要在api/user.ts中增加一个请求验证码的接口,如下:

export const userSendCodeUrl = '/user/send-code'

export type UserSendCodeParams = Pick<UserMobileLoginParams, 'mobile'>

export const userSendCodeApi = (params: UserSendCodeParams) => {
  return usePost<UserSendCodeParams, any>(userSendCodeUrl, params)
}

当我们点击发送验证码的时候,我们需要将当前的按钮禁用,并在一定的时间间隔内,不能再让用户去请求接口,那么我们的实现逻辑如下:

const counter = ref(120)
const counterDownState = ref(false)
const startCountdown = () => {
  counter.value = 120
  const timer = setInterval(() => {
    if (counter.value <= 0) {
      counterDownState.value = false
      clearInterval(timer)
      return
    }
    counter.value--
  }, 1000)
}

const sendCode = async () => {
  // 验证手机号是否已经填写
  const msgIns = message.loading(t('login.mobile.verification-code.loading'))
  try {
    await mFormRef.value?.validate(undefined, rule => rule.key === 'mobile')
    startCountdown()
    await userSendCodeApi({ mobile: mModel.mobile })
    msgIns.destroy()
    counterDownState.value = true
    message.success(t('login.mobile.verification-code.success'))
  }
  catch (e) {
    msgIns.destroy()
  }
}

这里我们需要新导入一个多语言,验证码发送成功的多语言 在locales/lang/pages的zh-CN.ts和en-US.ts的文件中分别导入:

export default {
    'login.mobile.verification-code.success': 'Verification code sent successfully!',
    'login.mobile.verification-code.loading': 'Sending verification code...',
}
export default{
    'login.mobile.verification-code.success': '验证码发送成功!',
    'login.mobile.verification-code.loading': '验证码发送中...',
}

接下来我们在pages/login/inde.vue中引入并使用:

<script setup lang="ts">
import { useMobileLogin } from './composables/mobile-login'
const { mModel, mFormRef, mRules, mLoading, counterDownState, counter, mLogin, sendCode } = useMobileLogin()

</script>
<template>
   <n-tab-pane name="mobile" :tab="$t('login.mobile.tab')">
    <n-form ref="mFormRef" label-align="left" :model="mModel" :rules="mRules" label-placement="left">
      <n-form-item-row path="mobile">
        <n-input v-model:value="mModel.mobile" :placeholder="$t('login.mobile.placeholder')">
          <template #prefix>
            <n-icon :component="MobileOutlined" />
          </template>
        </n-input>
      </n-form-item-row>
      <n-form-item-row path="code">
        <n-input-group>
          <n-input v-model:value="mModel.code" :placeholder="$t('login.mobile.verification-code.placeholder')">
            <template #prefix>
              <n-icon :component="LockOutlined" />
            </template>
          </n-input>
          <n-button :disabled="counterDownState" @click="sendCode">
            {{ counterDownState ? `${counter}s${$t('login.mobile.resend')}` : $t('login.mobile.verification-code.get-verification-code') }}
          </n-button>
        </n-input-group>
      </n-form-item-row>
      <n-form-item-row path="rememberMe">
        <div class="w-100% flex items-center justify-between">
          <n-checkbox v-model:value="mModel.rememberMe">
            {{ $t('login.remember-me') }}
          </n-checkbox>
          <a class="cursor-pointer text-[var(--text-color-3)]">
            {{ $t('login.forgot-password') }}
          </a>
        </div>
      </n-form-item-row>
    </n-form>
    <n-button type="primary" :loading="mLoading" block secondary strong @click="mLogin">
      {{ $t('login.login') }}
    </n-button>
  </n-tab-pane>
</template>

测试效果

⚠️ **GitHub.com Fallback** ⚠️