写点什么

【HarmonyOS NEXT】实战——登录页面

作者:帅比九日
  • 2024-11-25
    四川
  • 本文字数:7840 字

    阅读完需:约 26 分钟

【HarmonyOS NEXT】实战——登录页面

在本文中,我们将深入探讨如何使用 HarmonyOS NEXT 来实现一个功能完备的登录页面。通过这个实战案例,你将结合页面布局、数据本地化存储、网络请求等多方面了解到 HarmonyOS NEXT 在构建现代应用时的强大能力和灵活性。



@TOC

1. 整体结构

定义了一个 LoginPage 组件,该组件使用了 @Entry@Component 装饰器来标记它是一个入口组件和可复用的 UI 组件。LoginPage 组件包含了一些状态变量(@State)和方法(buildhandleLoginhandleForgotPassword)。

2. 状态变量

  • account: 用户名输入框的值,默认为空字符串。

  • password: 密码输入框的值,默认为空字符串。

  • text: 顶部的欢迎文字,默认为空字符串。

  • loading: 是否正在加载,默认为 false

  • rememberPassword: 是否记住密码,默认为 false

  • stopLogin: 是否停止自动登录,默认为 false

3. 方法

  • aboutToAppear: 组件即将显示时的生命周期方法,用于初始化状态变量。

  • build: 组件的构建方法,定义了页面的布局和组件。

  • handleLogin: 处理登录逻辑的方法。

  • handleForgotPassword: 处理忘记密码逻辑的方法。

4. 代码解析

4.1 aboutToAppear 方法

async aboutToAppear() {  const params = router.getParams() as JumpParams;  this.stopLogin = params.stopLogin || false;  this.rememberPassword = await PreferencesUtils.get('rememberPassword') as boolean;
if (this.rememberPassword) { this.account = await PreferencesUtils.get("account") as string; this.password = await PreferencesUtils.get("password") as string; }
if (this.account && this.password && !this.stopLogin) { this.handleLogin(); }}
复制代码


  • router.getParams(): 获取路由传递的参数,类型为 JumpParams。这里主要是为了通过路由获取参数,来判断是否进行自动登录操作。默认用户正常进入登陆页的时候需要自动登录,但是如果是点击了退出登录来到了登陆页则不进行自动登录。或许还有更多的特殊情况,这一点可以通过路由传参实现。

  • PreferencesUtils.get: 从本地存储中获取数据,这里主要是获取用户是否记住密码的状态,还有记录下的账户与密码。

  • 条件判断:

  • 如果用户选择了记住密码,从本地存储中读取用户名和密码。

  • 如果用户名和密码都已存在且 路由参数stopLoginfalse,自动调用 handleLogin 方法进行登录。

4.2 build 方法

build() {  Stack() {    Column() {      // 顶部的欢迎文字      Text(this.text)      Column() {        RelativeContainer() {          Text('您好!')            .fontSize(24)            .fontWeight(FontWeight.Bold)            .fontColor(Color.Red)            .alignRules({              top: { anchor: '__container__', align: VerticalAlign.Top },              center: { anchor: '__container__', align: VerticalAlign.Center }            })            .width('100%');
Text('欢迎使用xx系统!') .fontSize(22) .fontColor(Color.Black) .alignRules({ bottom: { anchor: '__container__', align: VerticalAlign.Bottom }, center: { anchor: '__container__', align: VerticalAlign.Center } }) .width('100%') .margin({ top: 80 }); } .height('30%') .width('100%') .padding({ left: 32, right: 32 }) .backgroundColor(Color.White);
// 用户名和密码输入框 Column() { // 用户名输入框 Column() { TextInput({ placeholder: '请输入用户名', text: this.account }) .onChange((value: string) => { this.account = value; }) .fontSize(18) .fontColor(Color.Black) .width('100%') .height(50) .padding({ left: 10, right: 10 }); Divider().height(1).color('#194487fe'); // 下划线 }
// 密码输入框 Column() { TextInput({ placeholder: '请输入密码', text: this.password }) .onChange((value: string) => { this.password = value; }) .fontSize(18) .fontColor(Color.Black) .width('100%') .height(50) .padding({ left: 10, right: 10 }) .type(InputType.Password); Divider().height(1).color('#194487fe'); // 下划线 }.margin({ top: 32 }); } .width('100%') .padding({ left: 32, right: 32 }) .backgroundColor(Color.White);
// 记住密码与忘记密码行 Row() { Row() { // 根据用户是否选择记住密码,显示不同的图标 Image($r(this.rememberPassword ? 'app.media.radio_normal_checkmark' : 'app.media.radio_normal')) .width(20) .height(20) .onClick(async () => { // 切换记住密码的状态 this.rememberPassword = !this.rememberPassword; await PreferencesUtils.put("rememberPassword", this.rememberPassword); });
Text('记住密码') .fontSize(14) .fontColor(Color.Red) .margin({ left: 10.5 }) .onClick(async () => { // 也可以点击文字时切换记住密码的状态 this.rememberPassword = !this.rememberPassword; await PreferencesUtils.put("rememberPassword", this.rememberPassword); if (!this.rememberPassword) { await PreferencesUtils.put("account", ''); await PreferencesUtils.put("password", ''); } }); }
Text('忘记密码') .fontSize(14) .fontColor(Color.Gray) .onClick(() => { // 实现忘记密码功能,跳转或弹出窗口 this.handleForgotPassword(); }); } .justifyContent(FlexAlign.SpaceBetween) .margin({ top: 21 }) .padding({ left: 32, right: 32 }) .width('100%');
// 登录按钮 Button('登 录') .width('60%') .height(50) .backgroundColor(Color.Red) .fontSize(18) .fontColor(Color.White) .onClick(() => { // 处理登录逻辑,并且在用户选择记住密码时保存密码 this.handleLogin(); }) .margin({ top: 56 });
// 底部图片 Image($r('app.media.login_bottom')) .width('100%'); } .height('100%') .width('100%') .backgroundColor(Color.White) .justifyContent(FlexAlign.SpaceBetween);
if (this.loading) { LoadingProgress().height(180).color('#cd0401'); } } }}
复制代码


  • Stack: 容器组件,用于堆叠其他组件。

  • Column: 垂直布局组件。

  • Text: 文本组件,用于显示文本内容。

  • RelativeContainer: 相对布局容器,用于精确控制子组件的位置。

  • TextInput: 输入框组件,用于输入用户名和密码。

  • onChange: 当输入框内容发生变化时的回调函数。

  • type(InputType.Password): 设置输入框为密码输入类型。

  • Divider: 分割线组件,用于在输入框下方添加下划线。

  • Row: 水平布局组件。

  • Image: 图像组件,用于显示记住密码的图标。

  • onClick: 点击图标时切换记住密码的状态,并保存到本地存储。

  • Button: 按钮组件,用于触发登录操作。

  • LoadingProgress: 加载进度组件,当 loadingtrue 时显示。

4.2.1 总体结构

这段代码定义了一个 build 方法,用于构建一个登录界面。界面包含以下几个主要部分:


  1. 顶部的欢迎文字

  2. 用户名和密码输入框

  3. 记住密码和忘记密码选项

  4. 登录按钮

  5. 底部图片

  6. 加载进度条(可选)


代码定义了一个登录界面,包含欢迎文字、用户名和密码输入框、记住密码和忘记密码选项、登录按钮和底部图片。通过使用 StackColumnRow 布局组件,以及 TextTextInputImageButton 等 UI 组件,构建了一个功能完整的登录页面。加载进度条部分是可选的,用于在登录过程中显示加载状态。

4.2.2 根部局
build() {  Stack() {    // 页面内容  }}
复制代码


  • Stack 是 HarmonyOS 中的一种布局组件,它可以将子组件叠放在一起。

  • Stack 作为根布局,包含整个页面的所有内容。

4.2.3 顶部的欢迎文字
Column() {  // 顶部的欢迎文字  Text(this.text)  Column() {    RelativeContainer() {      Text('您好!')        .fontSize(24)        .fontWeight(FontWeight.Bold)        .fontColor(Color.Red)        .alignRules({          top: { anchor: '__container__', align: VerticalAlign.Top },          center: { anchor: '__container__', align: VerticalAlign.Center }        })        .width('100%');
Text('欢迎使用xx系统!') .fontSize(22) .fontColor(Color.Black) .alignRules({ bottom: { anchor: '__container__', align: VerticalAlign.Bottom }, center: { anchor: '__container__', align: VerticalAlign.Center } }) .width('100%') .margin({ top: 80 }); } .height('30%') .width('100%') .padding({ left: 32, right: 32 }) .backgroundColor(Color.White); }}
复制代码


  • Column 是一个垂直布局组件,用于将子组件垂直排列。

  • Text(this.text) 显示一个变量 this.text,可能是动态的欢迎文字。

  • 内部的 Column 包含一个 RelativeContainer,用于更灵活地对齐子组件。

  • RelativeContainer 是一个相对布局组件,可以使用 alignRules 来指定子组件的对齐方式。

  • Text('您好!') 设置了字体大小、加粗、字体颜色和对齐方式。

  • Text('欢迎使用xx系统!') 设置了字体大小、字体颜色和对齐方式,并且设置了上边距。

  • RelativeContainer 设置了高度、宽度、内边距和背景颜色。

4.2.4 用户名和密码输入框
Column() {  // 用户名输入框  Column() {    TextInput({ placeholder: '请输入用户名', text: this.account })      .onChange((value: string) => {        this.account = value;      })      .fontSize(18)      .fontColor(Color.Black)      .width('100%')      .height(50)      .padding({ left: 10, right: 10 });    Divider().height(1).color('#194487fe'); // 下划线  }
// 密码输入框 Column() { TextInput({ placeholder: '请输入密码', text: this.password }) .onChange((value: string) => { this.password = value; }) .fontSize(18) .fontColor(Color.Black) .width('100%') .height(50) .padding({ left: 10, right: 10 }) .type(InputType.Password); Divider().height(1).color('#194487fe'); // 下划线 }.margin({ top: 32 });}.width('100%').padding({ left: 32, right: 32 }).backgroundColor(Color.White);
复制代码


  • 外部的 Column 包含用户名和密码输入框。

  • 内部的 Column 用于单独包装用户名输入框和密码输入框,以便更好地控制样式。

  • TextInput 是一个输入框组件,用于用户输入文本。

  • placeholder 是输入框的占位符文本。

  • text 是输入框当前显示的文本,绑定到 this.accountthis.password

  • onChange 是输入框的值改变时的回调函数,用于更新 this.accountthis.password

  • fontSizefontColorwidthheightpadding 设置了输入框的样式。

  • type(InputType.Password) 将输入框类型设置为密码输入框。

  • Divider 是一个分隔线组件,用于在输入框下方添加一条线,模拟下划线效果。

4.2.5 记住密码与忘记密码行
Row() {  Row() {    // 根据用户是否选择记住密码,显示不同的图标    Image($r(this.rememberPassword ? 'app.media.radio_normal_checkmark' : 'app.media.radio_normal'))      .width(20)      .height(20)      .onClick(async () => {        // 切换记住密码的状态        this.rememberPassword = !this.rememberPassword;        await PreferencesUtils.put("rememberPassword", this.rememberPassword);      });
Text('记住密码') .fontSize(14) .fontColor(Color.Red) .margin({ left: 10.5 }) .onClick(async () => { // 也可以点击文字时切换记住密码的状态 this.rememberPassword = !this.rememberPassword; await PreferencesUtils.put("rememberPassword", this.rememberPassword); if (!this.rememberPassword) { await PreferencesUtils.put("account", ''); await PreferencesUtils.put("password", ''); } }); }
Text('忘记密码') .fontSize(14) .fontColor(Color.Gray) .onClick(() => { // 实现忘记密码功能,跳转或弹出窗口 this.handleForgotPassword(); });}.justifyContent(FlexAlign.SpaceBetween).margin({ top: 21 }).padding({ left: 32, right: 32 }).width('100%');
复制代码


  • Row 是一个水平布局组件,用于将子组件水平排列。

  • 内部的 Row 包含记住密码的图标和文本。

  • Image 是一个图像组件,根据 this.rememberPassword 的值显示不同的图标。

  • onClick 是点击事件的回调函数,用于切换记住密码的状态并保存到偏好设置中。

  • Text('记住密码') 显示记住密码的文本,设置了字体大小、颜色和左边距,并绑定了点击事件。

  • 外部的 Row 包含忘记密码的文本,设置了字体大小、颜色,并绑定了点击事件。

  • justifyContent(FlexAlign.SpaceBetween) 使子组件在水平方向上均匀分布。

  • marginpadding 设置了外边距和内边距。

  • width 设置了组件的宽度。

4.2.6 登录按钮
Button('登 录')  .width('60%')  .height(50)  .backgroundColor(Color.Red)  .fontSize(18)  .fontColor(Color.White)  .onClick(() => {    // 处理登录逻辑,并且在用户选择记住密码时保存密码    this.handleLogin();  })  .margin({ top: 56 });
复制代码


  • Button 是一个按钮组件,用于触发登录操作。

  • widthheight 设置了按钮的宽度和高度。

  • backgroundColorfontColor 设置了按钮的背景颜色和字体颜色。

  • onClick 是按钮的点击事件回调函数,用于处理登录逻辑。

  • margin 设置了按钮的上边距。

4.2.7 底部图片
Image($r('app.media.login_bottom'))  .width('100%');
复制代码


  • Image 是一个图像组件,用于显示底部图片。

  • width 设置了图片的宽度。

  • $r 是资源引用函数,用于引用应用中的资源。

4.2.8 加载进度条(可选)
if (this.loading) {  LoadingProgress().height(180).color('#cd0401');}
复制代码


  • LoadingProgress 是一个加载进度条组件,用于显示加载状态。

  • heightcolor 设置了进度条的高度和颜色。

  • if (this.loading) 控制加载进度条的显示,只有当 this.loadingtrue 时才会显示加载进度条。

4.3 handleLogin 方法

async handleLogin() {  if (this.rememberPassword) {    // 如果用户选择了记住密码,保存账户和密码到本地存储    await PreferencesUtils.put("account", this.account);    await PreferencesUtils.put("password", this.password);  }
// 执行登录逻辑 this.loading = true; Login<LoginResponse>({ username: this.account, password: this.password, uuid: '', code: '', }).then(async (res) => { if (res.code !== 0) { promptAction.showToast({ message: res.msg, duration: 1000, }); this.loading = false; } else { // 保存用户数据和 token await PreferencesUtils.put("userData", res.data); await PreferencesUtils.put("token", res.data.token);
// 获取权限 const permission = await GetPermission<PermissionResponse>(); await PreferencesUtils.put("permission", permission); await PreferencesUtils.put("SystemUser_Permission", permission.data.dataPermission);
// 获取菜单权限 const menuRes = await GetMenu<PermissionResponse>(); await PreferencesUtils.put("User_Manage", menuRes.data || []);
// 跳转到主页 router.replaceUrl({ url: 'pages/MainPage' }).then(() => { console.info('Succeeded in jumping to the pages/MainPage.'); }).catch((err: BusinessError) => { console.error(`Failed to jump to the second page. Code is ${err.code}, message is ${err.message}`); });
this.loading = false; } }).catch((err: string) => { promptAction.showToast({ message: err, duration: 1000, }); this.loading = false; console.error(`Failed to login, message is ${err}`); });}
复制代码


  • 保存用户名和密码:

  • 如果用户选择了记住密码,使用 PreferencesUtils.put 方法将用户名和密码保存到本地存储。

  • 执行登录逻辑:

  • 设置 loadingtrue,显示加载进度。

  • 调用 Login API 进行登录,传入用户名、密码、UUID 和验证码。

  • 成功回调:

  • 如果 res.code 不为 0,显示错误提示。

  • 如果 res.code0,保存用户数据和 token 到本地存储。

  • 调用 GetPermission API 获取权限,并保存到本地存储。

  • 调用 GetMenu API 获取菜单权限,并保存到本地存储。

  • 使用 router.replaceUrl 方法跳转到主页,并处理跳转成功和失败的情况。

  • 失败回调:

  • 显示错误提示,并设置 loadingfalse

4.4 handleForgotPassword 方法

handleForgotPassword() {  // 前往忘记密码页面  router.pushUrl({ url: 'pages/Forgot' });}
复制代码


  • 跳转到忘记密码页面:

  • 使用 router.pushUrl 方法跳转到忘记密码页面。

5. 关键函数

  • PreferencesUtils.getPreferencesUtils.put: 用于从本地存储中读取和保存数据。

  • router.getParams: 用于获取路由传递的参数。

  • router.replaceUrlrouter.pushUrl: 用于在页面之间进行跳转。

  • promptAction.showToast: 用于显示短暂的提示信息。

7. 页面效果

进入页面:



登录接口请求中:


6. 总结

这段代码实现了一个完整的登录页面,包括用户名和密码的输入、记住密码功能、忘记密码功能以及登录逻辑。它使用了 HarmonyOS NEXT 的组件和 API,通过状态变量管理页面的状态,通过异步方法处理登录、权限获取和页面跳转等操作。

发布于: 刚刚阅读数: 5
用户头像

帅比九日

关注

还未添加个人签名 2024-11-15 加入

还未添加个人简介

评论

发布
暂无评论
【HarmonyOS NEXT】实战——登录页面_鸿蒙_帅比九日_InfoQ写作社区