引言

在现代的移动应用中,登录和注册必不可少,而鸿蒙又是现在的发展趋势,然而在网上却很难找到,一个完整的ArkUI实现的登录注册案例,并且是实现了功能的。本文将介绍一个使用ArkUI框架实现的简单而高效的登录和注册界面案例,并详细解析其代码绘制界面的过程以及功能实现的细节,通过过这个案例,读者可以了解到如何在ArkUI中使用HTTP请求和Axios库与后端服务器进行交互,以及如何管理组件状态以实现动态界面更新。该案例不仅实现了基本的登录和注册功能,还解决了验证码倒计时动态错误提示等常见痛点。

目标读者

本文适合所有对ArkUI框架感兴趣,希望了解如何实现登录和注册功能的开发者。无论你是初学者还是有经验的开发者,都可以通过本文获得对ArkUI框架的更深入理解,并学习如何处理HTTP请求和状态管理。

文章结构

文章将按照以下结构进行:

  1. 技术栈介绍:简述ArkUI、HTTP请求和Axios库的基本概念和技术优势。
  2. 代码结构与界面设计:详细说明代码的整体结构和每个部分的界面设计思路。
  3. 功能实现:深入解析发送验证码和登录/注册功能的具体实现细节。
  4. Axios与请求拦截器:了解到如何在项目中进行基础的HTTP请求配置,并确保在用户未登录的情况下,阻止对需要认证的API的访问,并引导用户到登录注册页面。

在接下来的内容中,我们将逐一探讨这些部分,帮助你更好地理解和实现这个登录和注册案例。现在,让我们从技术栈介绍开始吧。


let_sgo出发_爱给网_aigei_com.gif


技术栈介绍

ArkUI

ArkUI 是一个由华为开发的高性能、高可靠性的跨平台UI开发框架,专为全场景生态设计。它旨在提供统一的开发体验,支持多种设备,包括智能手机、平板、智能穿戴设备等。ArkUI采用声明式编程模型,使得开发者可以通过简洁的代码来描述UI界面,从而提高开发效率和代码可维护性。
主要特点

  • 声明式编程模型:通过描述UI的状态和行为来实现界面更新,简化代码逻辑。
  • 高性能:优化的渲染引擎和高效的组件复用机制,确保应用的流畅运行。
  • 跨平台支持:支持多种设备和操作系统,减少重复开发工作。
  • 丰富的组件库:提供大量的内置组件,满足各种复杂的UI需求。

    HTTP请求

    HTTP请求 是一种通过网络协议(如HTTP或HTTPS)与服务器进行通信的方式。在移动应用开发中,HTTP请求常用于获取和发送数据,实现用户登录、注册等功能。ArkUI框架提供了@ohos.net.http模块来处理HTTP请求。

主要特点

  • 简单易用:提供简洁的API接口,方便开发者进行HTTP请求。
  • 支持多种方法:包括GET、POST、PUT、DELETE等HTTP方法。
  • 支持自定义请求头和数据:可以灵活地设置请求头和请求数据,满足不同的业务需求。
  • 错误处理:提供详细的错误信息,帮助开发者调试和处理请求失败的情况。

    Axios

    Axios 是一个基于Promise的HTTP客户端,可以用于浏览器和Node.js中。它在移动端应用开发中常用于与后端服务器进行数据交互。Axios提供了丰富的功能和灵活的配置选项,使得HTTP请求更加简单和高效。

主要特点

  • 基于Promise:支持异步操作,便于处理请求的成功和失败情况。
  • 拦截器:可以拦截请求和响应,方便进行预处理和后处理。
  • 转换请求和响应数据:支持对请求和响应数据进行自动转换,简化数据处理过程。
  • 取消请求:允许取消正在进行的请求,避免无用的数据传输。
  • 自动转换JSON数据:默认情况下,自动将请求和响应的数据格式化为JSON,方便使用。

好了,由于笔者了解也有限,可能也并不太深入,大家有兴趣的可以自行去学习,技术就介绍到这里。
接下来,我们将详细解析代码的整体结构和界面设计思路。


代码结构与界面设计

在本部分中,我们将详细说明代码的整体结构和每个部分的界面设计思路。通过这些说明,你可以更好地理解如何在ArkUI中构建复杂的UI界面,并实现与后端服务器的交互。

整体结构

首先,我们来看一下整个代码的结构:

@Entry
@Component
struct LoginRegister {
  @State phone: string = ''
  @State code: string = ''
  @State errorMessage: string = ''
  @State isAgree: boolean = false

  @State countdown: number = 60
  @State isCounting: boolean = false
  @State loginData: LoginData | null = null;
  private interval: number | null = null; // 存储 interval 的引用

  // 发送验证码的API调用
  sendVerificationCode(phone: string) { ... }

  // 登录或注册的API调用
  loginOrRegister(phone: string, code: string) { ... }

  startCountdown() : void { ... }

  build() { ... }

  @Builder
  logoComponent() { ... }
}

主要部分

  • 状态定义:使用@State装饰器定义组件的状态变量,如手机号、验证码、错误信息、协议同意状态等。

  • 方法定义:定义了几个关键方法,包括发送验证码、登录或注册以及启动倒计时。

  • 界面构建:通过build方法构建整个登录和注册界面。

  • 自定义组件:使用@Builder装饰器定义了一个可复用的logoComponent。

    界面设计

    我们挑一些主要的实现,来给大家介绍一下:

    手机或验证码的输入框

    Row() {
    IconColumnView({imagePath: "app.media.ic_phone"})
    
    Column() {
      TextInput({
        placeholder: '请输入手机号',
        text: this.phone,
      })
        .placeholderColor('rgba(0, 0, 0, 0.6)')
        .fontColor(Color.Black)
        .width('100%')
        .borderRadius(15)
        .backgroundColor('#EDEDED')
        .caretColor('#000000')
        .height(39)
        .onChange((value: string) => {
          this.phone = value;
          if (this.phone === '' || this.phone.length !== 11 || !/^\d+$/.test(this.phone)) {
            // 检查手机号是否为空,或者长度是否不等于11位,并且是否只包含数字
            if (this.phone === '') {
              this.errorMessage = ''; // 如果输入为空,清空错误信息
            } else {
              this.errorMessage = '请输入有效的手机号'; // 如果输入不符合条件,设置错误信息
            }
          } else {
            this.errorMessage = ''; // 如果符合条件,清空错误信息
          }
        })
    }
    .width('85.8%')
    .backgroundColor('#EDEDED')
    .borderRadius({
      topLeft: 0,
      topRight: 15,
      bottomLeft: 0,
      bottomRight: 15
    })
    }
    .width('91%')
    .borderRadius(15)
    .backgroundColor('#EDEDED')
    .margin({ top: 45 })
    

    感觉笔者实现的可能有点复杂了,需求要在输入框的前面显示一个图标,用来表示这是手机或验证码的输入框,而我的实现方式是将两列叠在一起,有什么组件或者好的实现方式可以告诉我。

    猫咪吸鼻涕动图  哈哈哈哈哈,感冒时吸鼻子的你_猫_动图表_爱给网_aigei_com.gif


    这里也没什么可讲的,在实现功能时我们还是要使用onChange事件:当用户输入手机号时,检查输入格式是否正确。如果不正确,则显示错误信息。
    下面就是手机号和验证码的实现完成的图示

    image.png

    登录按钮

    登录按钮允许用户使用手机号和验证码进行登录或注册。

    Row() {
    Button('登录', { type: ButtonType.Normal })
      .width('67.7%')
      .height(39)
      .backgroundColor($r('sys.color.black'))
      .fontColor('#BEFF33')
      .fontSize(20)
      .borderRadius(15)
      .onClick(() => {
        if (this.phone.length === 11 && this.code.length > 0) {
          this.loginOrRegister(this.phone, this.code);
        }
      })
    }
    .margin({ top: 28 })
    .justifyContent(FlexAlign.Center)
    .width('100%')
    

    主要也没什么可讲的主要就是调整样式和onClick事件,当用户点击按钮时,检查手机号和验证码是否正确。如果正确,则调用loginOrRegister方法。

    image.png

    错误提示信息

    一个完整的登录注册不能只有输入框,登录按钮就行了,还需要错误提示信息用于显示用户输入错误或请求失败的信息。

    Row() {
    if (this.errorMessage === '') {
      Text('')
    }
    else {
      Text(this.errorMessage)
        .fontSize(14)
        .fontColor('#F50000')
    }
    }
    .width('91%')
    

    当然这些错误提示信息需要你在其他的代码逻辑中给出,实现。后面我的介绍中也会提到。

    协议和隐私政策

    一个好的应用,这一点是必不可少的,我在这部分将这些封装到一个小view中以方便后面其他界面的复用,嗯感觉也有点复杂,怕登录页面代码太多就把这些放在view中了,有没有只用一个Text就可以放完这些文字的组件。求指导。

    无语的表情14_爱给网_aigei_com.jpg

    import { router } from '@kit.ArkUI'
    @Component
    export struct agreementView {
    @Link isAgree: boolean
    build() {
      Row() {
        Checkbox()
          .width(17)
          .height(15)
          .shape(CheckBoxShape.ROUNDED_SQUARE)
          .select(this.isAgree)
          .mark({ strokeColor: '#BEFF33' })
          .selectedColor($r('sys.color.black'))
          .borderRadius(6)
          .onClick(() => {
            this.isAgree = !this.isAgree
          })
    
        Text('我已阅读并同意')
          .fontColor($r('app.color.menu_font_color'))
          .fontSize(14)
          .lineHeight(20.27)
        Text('用户协议')
          .fontSize(14)
          .lineHeight(20.27)
          .onClick(() => {
            router.pushUrl({
              url: 'pages/UserAgreementPage' // 目标页面的 URL
            });
          })
        Text('和')
          .fontSize(14)
          .lineHeight(20.27)
          .fontColor($r('app.color.menu_font_color'))
        Text('隐私政策')
          .fontSize(14)
          .lineHeight(20.27)
          .onClick(() => {
            router.pushUrl({
              url: 'pages/PrivacyPolicyPage' // 目标页面的 URL
            });
          })
      }
      .width('100%')
      .height(21)
      .alignItems(VerticalAlign.Center)
      .justifyContent(FlexAlign.Center)
      .margin({ top: 26 })
    }
    }
    

    使用

            //协议和隐私政策
            agreementView({isAgree :this.isAgree})
    

    样式调整

    为了让整个登录注册更美观一点,当然这是我们产品的功劳。下面给出我案例的登录注册部分。我讲一下这个底层绿色的怎么实现,界面设计就讲到这,其他都是一些简单的布局和组件的使用。

    image.png


    代码实现

              //底层遮罩层(绿色)
              Column() {
              }
              .width('74.4%')
              .height(276)
              .backgroundColor('rgba(173, 212, 91, 0.8)')
              .borderRadius(6)
              .position({
                x: 66,
                y: 28
              })
              .zIndex(0)
    

    我采用了绝对定位+zindex设置层级的方式实现的,当然也可以使用,stack层叠布局来实现。Position类型基于父组件左上角确定位置,zindex(0)设置层级最低。即可实现。我感觉加了一个这个真的挺好看的,产品的眼光真好。


通过以上对代码结构和界面设计的详细说明,你可以了解到如何在ArkUI中构建简单的登录注册UI界面,接下来,我们将深入解析发送验证码和登录/注册功能的具体实现细节。


功能实现

接下来就进入主要的部分,功能实现环节,可能在本部分中,我们将深入解析发送验证码和登录/注册功能的具体实现细节。通过这些解析,你可以了解到如何在ArkUI中使用HTTP请求和Axios库与后端服务器进行交互,并如何管理组件状态以实现动态界面更新。

发送验证码功能

发送验证码功能通过sendVerificationCode方法实现。该方法首先检查用户是否同意了协议和隐私政策,然后通过HTTP请求发送验证码到指定手机号。

//发送验证码的api调用
sendVerificationCode(phone: string) {
  if (this.isAgree === false) {
    this.errorMessage = '请先同意协议和隐私政策';
    return;
  } else {
    this.errorMessage = '';
  }
  let httpRequest = http.createHttp();
  httpRequest.request(
    '你的api地址',
    {
      method: http.RequestMethod.POST,
      header: { 'Content-Type': 'application/json' },
      extraData: {
        phone: phone,
      }
    }, (err, data) => {
    if (!err) {
      console.log('验证码发送成功');
      this.startCountdown();
    } else {
      console.error('验证码发送失败', err);
      this.errorMessage = '验证码发送失败,请重试';
    }
  });
}

详细步骤

  1. 检查协议同意状态
    使用this.isAgree检查用户是否同意了协议和隐私政策。并更新错误信息
  2. 创建创建HTTP请求:
    使用http.createHttp()创建一个HTTP请求实例。
  3. 发送POST请求
    使用httpRequest.request方法发送POST请求到指定的URL,设置请求头为{'Content-Type': 'application/json'},设置请求数据为{ phone: phone }。
  4. 处理响应
    如果请求成功 (!err),则在控制台输出“验证码发送成功”,并调用this.startCountdown()启动倒计时,如果请求失败 (err),则在控制台输出错误信息,并设置this.errorMessage为“验证码发送失败,请重试”。

    登录注册功能

    登录或注册功能通过loginOrRegister方法实现。该方法使用Axios库发送POST请求到指定的URL,并处理请求的成功和失败情况。
    ```javascript
    //登录注册的api调用
    loginOrRegister(phone: string, code: string) {
    console.log('尝试登录或注册的手机号和验证码:', phone, code); // 添加测试日志

axios.post('你的api地址', {
phone: phone,
code: code,
})
.then((response: AxiosResponse) => {
console.log('响应数据:', response.data); // 添加测试日志
const parsedData: LoginData = response.data; // 使用 LoginData 接口明确类型
console.log('解析后的数据:', parsedData); // 添加测试日志
if (parsedData && parsedData.flag) {
console.log('登录或注册成功,设置登录数据并跳转'); // 添加测试日志
this.loginData = parsedData;
dataPreferences?.putSync("token", parsedData.data);
dataPreferences?.flush();
console.log('准备跳转到 /pages/Tabs'); // 添加测试日志
router.pushUrl({
url: 'pages/Tabs' // 目标页面的 URI
});
console.log('跳转完成后'); // 添加测试日志
} else {
console.error('登录或注册失败', parsedData.msg); // 添加测试日志
if (parsedData.msg && parsedData.msg.includes('验证码错误')) {
this.errorMessage = '验证码错误,请重试';
} else {
this.errorMessage = '登录或注册失败,请重试';
}
}
})
.catch((error: AxiosError) => { // 使用 AxiosError 明确类型
console.error('登录或注册失败', error); // 添加测试日志
console.error('错误信息:', error.message); // 添加测试日志
console.error('错误响应:', error.response); // 添加测试日志
console.error('错误配置:', error.config); // 添加测试日志
console.error('错误状态码:', error.response?.status); // 添加测试日志
console.error('错误状态文本:', error.response?.statusText); // 添加测试日志
this.errorMessage = 登录或注册失败,请重试: ${error.message};
});
}

**详细步骤**
1. 发送POST请求
使用axios.post方法发送POST请求到指定的URL,设置请求数据为{ phone: phone, code: code }。
2. 处理成功响应
在then回调中,使用console.log输出响应数据。解析响应数据为LoginData类型。如果parsedData.flag为true,表示登录或注册成功。设置this.loginData为解析后的数据。使用dataPreferences.putSync保存token,并调用dataPreferences.flush刷新数据。使用router.pushUrl跳转到目标页面 (/pages/Tabs)。
如果parsedData.flag为false,表示登录或注册失败。使用console.error输出错误信息。
根据错误信息的具体内容,设置this.errorMessage为相应的提示信息。
3. 处理失败响应
在catch回调中,使用console.error输出详细的错误信息,包括错误信息、响应数据、配置信息、状态码和状态文本。设置this.errorMessage为“登录或注册失败,请重试”,并附带具体的错误信息。
### 倒计时功能    
倒计时功能通过startCountdown方法实现。该方法启动一个定时器,每秒更新一次验证码按钮的倒计时状态。
```javascript
startCountdown() : void {
  if (this.isCounting) return; // 如果已经在倒计时,则不重复启动

  this.isCounting = true;  // 开始计时
  this.countdown = 60;  // 初始化倒计时为60秒

  // 每秒更新一次倒计时
  this.interval = setInterval(() => {
    if (this.countdown > 0) {
      this.countdown--;
    } else {
      clearInterval(this.interval!);
      this.isCounting = false;  // 结束倒计时
      this.interval = null; // 重置 interval 引用
    }
  }, 1000);
}

显示倒计时
在界面点击发送显示倒计时

              Column() {
                 // 发送验证码按钮
                 Text(this.isCounting ? `(${this.countdown})` : '发送')
                   .textAlign(TextAlign.Center)
                   .width('100%')
                   .height(39)
                   .fontColor('#000000')
                   .fontSize(16)
                   .borderRadius(6)
                   .backgroundColor('#CCCCCC')//倒计时显示
                   .enabled(!this.isCounting && this.phone.length === 11 && /^[0-9]+$/.test(this.phone))
                   .onClick(() => {
                     this.sendVerificationCode(this.phone);
                   })
               }
               .margin({ left: 5 })

详细步骤

  1. 检查是否已经在倒计时
    如果this.isCounting为true,则直接返回,避免重复启动倒计时。
  2. 初始化倒计时状态
  3. 启动定时器
    使用setInterval每秒执行一次回调函数。在回调函数中,检查this.countdown是否大于0。如果是,则每秒递减1。如果this.countdown等于0,则清除定时器 (clearInterval(this.interval!)),设置this.isCounting为false,并重置this.interval为null。

通过以上解析,你已经了解到如何在ArkUI中实现发送验证码、倒计时和登录/注册功能。接下来,我来介绍一下Axios配置与拦截器,以便更加完善这个登录注册功能


Axios配置与拦截器

在本部分中,我们将介绍与登录注册相关的Axios配置,特别是请求拦截器的实现。这部分内容将帮助你理解如何确保在用户未登录的情况下,阻止对需要认证的API的访问,并引导用户到登录注册页面。

Axios配置

首先,我们来看一下Axios的配置部分

import { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from '@ohos/axios';
import { dataPreferences } from '../../entryability/EntryAbility';
import axios from '../AxiosRequest';
import { router } from '@kit.ArkUI';

axios.defaults.baseURL = "http://xxx/xxx";

const instance = axios.create({
  headers: { 'Content-Type': 'application/json' },
});

const noTokenRequiredUrls = [
  'http://xxx/login/phone',
  'http://xxx/login/sendMsg',
  '/xxx'
];

const noNeedRouter = [
  '/xxx'
];

详细说明

  • 基础URL (baseURL):设置所有请求的基础URL为http://xxx/xxx%EF%BC%8C%E7%AE%80%E5%8C%96%E8%AF%B7%E6%B1%82%E8%B7%AF%E5%BE%84%E3%80%82
  • 实例创建 (instance):使用axios.create创建一个Axios实例,并设置默认请求头为{'Content-Type': 'application/json'}。
  • 无需Token的URL列表 (noTokenRequiredUrls):定义一个数组,包含不需要token的URL,如登录和发送验证码的接口。
  • 无需路由的URL列表 (noNeedRouter):定义一个数组,包含不需要重定向到登录页面的URL,如获取xxx数据的接口。

    请求拦截器

    请求拦截器用于在发送请求之前检查用户是否已经登录,并根据需要添加token或重定向到登录页面。
    // 添加请求拦截器
    instance.interceptors.request.use(async (config: InternalAxiosRequestConfig) => {
    const isNoTokenRequired = noTokenRequiredUrls.some(url => config.url?.includes(url));
    let token: string = "";
    if (!isNoTokenRequired) {
      token = await dataPreferences?.get("token", "") as string;
      if (!token || token == "" || token == "null" || token == "undefined") {
        if (!noNeedRouter.some(url => config.url?.includes(url))) {
          router.pushUrl({
            url: "pages/LoginRegister"
          });
        }
        return Promise.reject('用户未登录');
      }
      config.headers["token"] = token;
    }
    return config;
    }, (error: AxiosError) => {
    // 对请求错误做些什么
    return Promise.reject(error);
    });
    
    详细步骤
  1. 检查是否需要Token:
  • 使用noTokenRequiredUrls数组检查当前请求的URL是否在无需token的URL列表中。
  • 如果请求的URL在列表中,则isNoTokenRequired为true,跳过token检查。
  1. 获取Token:
    如果请求需要token,则从dataPreferences中获取token。
  2. 验证Token:
  • 检查获取到的token是否为空、null或undefined。
  • 如果token无效,则根据请求URL是否在noNeedRouter列表中决定是否重定向到登录页面。
    如果请求URL不在noNeedRouter列表中,则使用router.pushUrl重定向到pages/LoginRegister。
    返回一个被拒绝的Promise,表示用户未登录。
  1. 添加Token到请求头:
    如果token有效,则将其添加到请求头中 (config.headers["token"] = token)。
  2. 返回配置:
    返回修改后的请求配置 (config)。
  3. 处理请求错误:
    如果请求配置过程中发生错误,则返回一个被拒绝的Promise,包含具体的错误信息。

为了更好地理解这些配置如何应用于具体的功能,我们来看一个与登录注册相关的接口示例

export async function planJourney(routeId: number, day: number) {
  const params = JSON.stringify({
    routeId: routeId,
    day: day
  });
  return (await instance.post(`/planJourney`, params)).data;
}
  • 请求路径:/planJourney是需要认证的API路径。
  • 请求方法:使用instance.post方法发送POST请求。
  • 请求数据:将routeId和day参数序列化为JSON字符串并作为请求数据发送。
  • 拦截器应用:
    在请求拦截器中,检查/planJourney是否在noTokenRequiredUrls列表中。
    由于/planJourney不在列表中,拦截器将获取token并添加到请求头中。
    如果token无效,则重定向到登录页面 (pages/LoginRegister)。

通过以上对Axios配置和拦截器的详细说明,你可以了解到如何在项目中进行基础的HTTP请求配置,并确保在用户未登录的情况下,阻止对需要认证的API的访问,并引导用户到登录注册页面。


总结

通过本文的详细介绍,我们深入探讨了如何在ArkUI框架中实现一个完整的登录和注册功能,并结合Axios库进行HTTP请求的处理。我们从技术栈的选择开始,逐步解析了代码的整体结构和界面设计,详细说明了发送验证码、登录/注册以及倒计时功能的实现细节。此外,我们还介绍了如何配置Axios拦截器,确保在用户未登录的情况下,阻止对需要认证的API的访问,并引导用户到登录注册页面。希望这个案例可以帮到大家。


openHarmony官方文档
HarmonyOS官方文档
axios官方文档
希望这些资源能够帮助你进一步学习和掌握ArkUI和Axios的相关知识。

中国风完结字样_爱给网_aigei_com.gif

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐