Skip to content

Latest commit

 

History

History
152 lines (137 loc) · 4.88 KB

35-%E8%B4%A6%E5%8F%B7%E7%99%BB%E5%BD%95%E4%B8%9A%E5%8A%A1%E9%80%BB%E8%BE%91%E5%BC%80%E5%8F%91.md

File metadata and controls

152 lines (137 loc) · 4.88 KB

目标

分析业务逻辑与页面渲染分离的好处 完成账号登录业务逻辑

分析

在过去vue2的开发中,我们通常会把所有的业务逻辑和UI界面都在一个vue文件中去完成,导致了整个页面中代码逻辑过多,后期我们想复用这一部分的代码,可能需要花费大量的时间去提取,或者直接复制粘贴,导致大量的代码冗余的问题,所以我们为了避免这种问题,我们可以考虑在vue3中使用组合式API的方式,来抽离业务代码,使得业务代码和UI界面分离更容易维护、扩展和复用等。 大家想了解更多关于组合式API的问题可以去组合式 API 常见问答,里面详细的介绍了传统API和组合式API的区别和优劣势。

开发

我们在pages/login文件夹下创建一个composables的组合式API的文件夹,用于存放我们login文件夹下所有的与业务相关的组合式API。 然后我们创建一个account-login.ts的文件用于编写我们的登录部分业务逻辑。如下:

import type { FormInst } from 'naive-ui'
import type { UserAccountLoginParams } from '~/api/user'

export const useAccountLogin = () => {
  const formRef = ref<FormInst>()
  const loading = ref(false)

  const model = reactive<UserAccountLoginParams>({
    username: null,
    password: null,
  })

  const login = async () => {

  }

  return {
    formRef,
    loading,
    model,
    login,
  }
}

我们写完这一部分后,会发现参数报错。是因为默认情况下我们的UserAccountLoginParams不支持null类型所以我们需要给我们的类型添加一个null类型。如下: 在utils目录下创建一个types.ts的文件用于存放我们自己定义的通用类型。

export type IncludeNull<T> = T | null

调整api/user.ts中的类型:

import type { IncludeNull } from '~/utils/types'

export interface UserAccountLoginParams {
  username: IncludeNull<string>
  password: IncludeNull<string>
  captcha?: IncludeNull<string>
}

export interface UserMobileLoginParams {
  mobile: IncludeNull<string>
  code: IncludeNull<string>
  type: 'mobile'
}

调整完成后我们的报错也就随之消失了。 接下来我们来配置一下完善一下校验规则:

  const rules = reactive<FormRules>({
    username: [
      {
        required: true,
        renderMessage: () => t('login.username.required'),
      },
      {
        min: 5,
        max: 20,
        renderMessage: () => t('login.username.length'),
      },
    ],
    password: [
      {
        required: true,
        renderMessage: () => t('login.password.required'),
      },
      {
        min: 5,
        max: 20,
        renderMessage: () => t('login.password.length'),
      },
    ],
  })

最后return出去。 接下来我们来完善一下我们的登录接口:

// 1.加载loading状态
// 2.校验表单是否正确
// 3.请求接口
// 4.请求成功设置token
// 5.跳转到首页

const login = async () => {
  loading.value = true
  try {
    await formRef.value?.validate()
    await userStore.login(model)
    loading.value = false
    const redirect = router.currentRoute.value?.params?.redirect as string
    await router.replace(redirect || '/')
  }
  catch (e) {
    loading.value = false
  }
}

接下来我们在项目中使用一下: 在pages/login/index.vue中使用:

<script lang="ts" setup>
const { rules, formRef, login, loading, model } = useAccountLogin()
</script>
<template>
  <n-form ref="formRef" :model="model" :rules="rules" label-align="left" label-placement="left">
    <n-form-item-row path="username">
      <n-input v-model:value="model.username" :placeholder="$t('login.username.placeholder')">
        <template #prefix>
          <n-icon :component="UserOutlined" />
        </template>
      </n-input>
    </n-form-item-row>
    <n-form-item-row path="password">
      <n-input v-model:value="model.password" type="password" show-password-on="click" :placeholder="$t('login.password.placeholder')">
        <template #prefix>
          <n-icon :component="LockOutlined" />
        </template>
      </n-input>
    </n-form-item-row>
    <n-form-item-row path="rememberMe">
      <div class="w-100% flex items-center justify-between">
        <n-checkbox v-model:value="model.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="loading" block secondary strong @click="login">
    {{ $t('login.login') }}
  </n-button>
</template>

测试是否可用