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

打印 上一主题 下一主题

主题 973|帖子 973|积分 2919

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

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


  
1. 整体布局

界说了一个 LoginPage 组件,该组件使用了 @Entry 和 @Component 装饰器来标志它是一个入口组件和可复用的 UI 组件。LoginPage 组件包含了一些状态变量(@State)和方法(build、handleLogin、handleForgotPassword)。
2. 状态变量



  • account: 用户名输入框的值,默以为空字符串。
  • password: 暗码输入框的值,默以为空字符串。
  • text: 顶部的接待文字,默以为空字符串。
  • loading: 是否正在加载,默以为 false。
  • rememberPassword: 是否记住暗码,默以为 false。
  • stopLogin: 是否停止主动登录,默以为 false。
3. 方法



  • aboutToAppear: 组件即将表现时的生命周期方法,用于初始化状态变量。
  • build: 组件的构建方法,界说了页面的布局和组件。
  • handleLogin: 处理处罚登录逻辑的方法。
  • handleForgotPassword: 处理处罚忘记暗码逻辑的方法。
4. 代码剖析

4.1 aboutToAppear 方法

  1. async aboutToAppear() {
  2.   const params = router.getParams() as JumpParams;
  3.   this.stopLogin = params.stopLogin || false;
  4.   this.rememberPassword = await PreferencesUtils.get('rememberPassword') as boolean;
  5.   if (this.rememberPassword) {
  6.     this.account = await PreferencesUtils.get("account") as string;
  7.     this.password = await PreferencesUtils.get("password") as string;
  8.   }
  9.   if (this.account && this.password && !this.stopLogin) {
  10.     this.handleLogin();
  11.   }
  12. }
复制代码


  • router.getParams(): 获取路由通报的参数,范例为 JumpParams。这里主要是为了通过路由获取参数,来判定是否举行主动登录操纵。默认用户正常进入登岸页的时间必要主动登录,但是假如是点击了退出登录来到了登岸页则不举行主动登录。大概还有更多的特殊情况,这一点可以通过路由传参实现。
  • PreferencesUtils.get: 从本地存储中获取数据,这里主要是获取用户是否记住暗码的状态,还有记录下的账户与暗码。
  • 条件判定:

    • 假如用户选择了记住暗码,从本地存储中读取用户名和暗码。
    • 假如用户名和暗码都已存在且 路由参数stopLogin 为 false,主动调用 handleLogin 方法举行登录。

4.2 build 方法

  1. build() {
  2.   Stack() {
  3.     Column() {
  4.       // 顶部的欢迎文字
  5.       Text(this.text)
  6.       Column() {
  7.         RelativeContainer() {
  8.           Text('您好!')
  9.             .fontSize(24)
  10.             .fontWeight(FontWeight.Bold)
  11.             .fontColor(Color.Red)
  12.             .alignRules({
  13.               top: { anchor: '__container__', align: VerticalAlign.Top },
  14.               center: { anchor: '__container__', align: VerticalAlign.Center }
  15.             })
  16.             .width('100%');
  17.           Text('欢迎使用xx系统!')
  18.             .fontSize(22)
  19.             .fontColor(Color.Black)
  20.             .alignRules({
  21.               bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
  22.               center: { anchor: '__container__', align: VerticalAlign.Center }
  23.             })
  24.             .width('100%')
  25.             .margin({ top: 80 });
  26.         }
  27.         .height('30%')
  28.         .width('100%')
  29.         .padding({ left: 32, right: 32 })
  30.         .backgroundColor(Color.White);
  31.         // 用户名和密码输入框
  32.         Column() {
  33.           // 用户名输入框
  34.           Column() {
  35.             TextInput({ placeholder: '请输入用户名', text: this.account })
  36.               .onChange((value: string) => {
  37.                 this.account = value;
  38.               })
  39.               .fontSize(18)
  40.               .fontColor(Color.Black)
  41.               .width('100%')
  42.               .height(50)
  43.               .padding({ left: 10, right: 10 });
  44.             Divider().height(1).color('#194487fe'); // 下划线
  45.           }
  46.           // 密码输入框
  47.           Column() {
  48.             TextInput({ placeholder: '请输入密码', text: this.password })
  49.               .onChange((value: string) => {
  50.                 this.password = value;
  51.               })
  52.               .fontSize(18)
  53.               .fontColor(Color.Black)
  54.               .width('100%')
  55.               .height(50)
  56.               .padding({ left: 10, right: 10 })
  57.               .type(InputType.Password);
  58.             Divider().height(1).color('#194487fe'); // 下划线
  59.           }.margin({ top: 32 });
  60.         }
  61.         .width('100%')
  62.         .padding({ left: 32, right: 32 })
  63.         .backgroundColor(Color.White);
  64.         // 记住密码与忘记密码行
  65.         Row() {
  66.           Row() {
  67.             // 根据用户是否选择记住密码,显示不同的图标
  68.             Image($r(this.rememberPassword ? 'app.media.radio_normal_checkmark' : 'app.media.radio_normal'))
  69.               .width(20)
  70.               .height(20)
  71.               .onClick(async () => {
  72.                 // 切换记住密码的状态
  73.                 this.rememberPassword = !this.rememberPassword;
  74.                 await PreferencesUtils.put("rememberPassword", this.rememberPassword);
  75.               });
  76.             Text('记住密码')
  77.               .fontSize(14)
  78.               .fontColor(Color.Red)
  79.               .margin({ left: 10.5 })
  80.               .onClick(async () => {
  81.                 // 也可以点击文字时切换记住密码的状态
  82.                 this.rememberPassword = !this.rememberPassword;
  83.                 await PreferencesUtils.put("rememberPassword", this.rememberPassword);
  84.                 if (!this.rememberPassword) {
  85.                   await PreferencesUtils.put("account", '');
  86.                   await PreferencesUtils.put("password", '');
  87.                 }
  88.               });
  89.           }
  90.           Text('忘记密码')
  91.             .fontSize(14)
  92.             .fontColor(Color.Gray)
  93.             .onClick(() => {
  94.               // 实现忘记密码功能,跳转或弹出窗口
  95.               this.handleForgotPassword();
  96.             });
  97.         }
  98.         .justifyContent(FlexAlign.SpaceBetween)
  99.         .margin({ top: 21 })
  100.         .padding({ left: 32, right: 32 })
  101.         .width('100%');
  102.         // 登录按钮
  103.         Button('登 录')
  104.           .width('60%')
  105.           .height(50)
  106.           .backgroundColor(Color.Red)
  107.           .fontSize(18)
  108.           .fontColor(Color.White)
  109.           .onClick(() => {
  110.             // 处理登录逻辑,并且在用户选择记住密码时保存密码
  111.             this.handleLogin();
  112.           })
  113.           .margin({ top: 56 });
  114.         // 底部图片
  115.         Image($r('app.media.login_bottom'))
  116.           .width('100%');
  117.       }
  118.       .height('100%')
  119.       .width('100%')
  120.       .backgroundColor(Color.White)
  121.       .justifyContent(FlexAlign.SpaceBetween);
  122.       if (this.loading) {
  123.         LoadingProgress().height(180).color('#cd0401');
  124.       }
  125.     }
  126.   }
  127. }
复制代码


  • Stack: 容器组件,用于堆叠其他组件。
  • Column: 垂直布局组件。
  • Text: 文本组件,用于表现文本内容。
  • RelativeContainer: 相对布局容器,用于精确控制子组件的位置。
  • TextInput: 输入框组件,用于输入用户名和暗码。

    • onChange: 当输入框内容发生变化时的回调函数。
    • type(InputType.Password): 设置输入框为暗码输入范例。

  • Divider: 分割线组件,用于在输入框下方添加下划线。
  • Row: 程度布局组件。
  • Image: 图像组件,用于表现记住暗码的图标。

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

  • Button: 按钮组件,用于触发登录操纵。
  • LoadingProgress: 加载进度组件,当 loading 为 true 时表现。
4.2.1 总体布局

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

  • 顶部的接待文字
  • 用户名和暗码输入框
  • 记住暗码和忘记暗码选项
  • 登录按钮
  • 底部图片
  • 加载进度条(可选)
代码界说了一个登录界面,包含接待文字、用户名和暗码输入框、记住暗码和忘记暗码选项、登录按钮和底部图片。通过使用 Stack、Column 和 Row 布局组件,以及 Text、TextInput、Image 和 Button 等 UI 组件,构建了一个功能完备的登录页面。加载进度条部门是可选的,用于在登录过程中表现加载状态。
4.2.2 根部局

  1. build() {
  2.   Stack() {
  3.     // 页面内容
  4.   }
  5. }
复制代码


  • Stack 是 HarmonyOS 中的一种布局组件,它可以将子组件叠放在一起。
  • Stack 作为根布局,包含整个页面的全部内容。
4.2.3 顶部的接待文字

  1. Column() {
  2.   // 顶部的欢迎文字
  3.   Text(this.text)
  4.   Column() {
  5.     RelativeContainer() {
  6.       Text('您好!')
  7.         .fontSize(24)
  8.         .fontWeight(FontWeight.Bold)
  9.         .fontColor(Color.Red)
  10.         .alignRules({
  11.           top: { anchor: '__container__', align: VerticalAlign.Top },
  12.           center: { anchor: '__container__', align: VerticalAlign.Center }
  13.         })
  14.         .width('100%');
  15.       Text('欢迎使用xx系统!')
  16.         .fontSize(22)
  17.         .fontColor(Color.Black)
  18.         .alignRules({
  19.           bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
  20.           center: { anchor: '__container__', align: VerticalAlign.Center }
  21.         })
  22.         .width('100%')
  23.         .margin({ top: 80 });
  24.     }
  25.     .height('30%')
  26.     .width('100%')
  27.     .padding({ left: 32, right: 32 })
  28.     .backgroundColor(Color.White);
  29.   }
  30. }
复制代码


  • Column 是一个垂直布局组件,用于将子组件垂直排列。
  • Text(this.text) 表现一个变量 this.text,可能是动态的接待文字。
  • 内部的 Column 包含一个 RelativeContainer,用于更机动地对齐子组件。
  • RelativeContainer 是一个相对布局组件,可以使用 alignRules 来指定子组件的对齐方式。

    • Text('您好!') 设置了字体巨细、加粗、字体颜色和对齐方式。
    • Text('接待使用xx体系!') 设置了字体巨细、字体颜色和对齐方式,并且设置了上边距。

  • RelativeContainer 设置了高度、宽度、内边距和配景颜色。
4.2.4 用户名和暗码输入框

  1. Column() {
  2.   // 用户名输入框
  3.   Column() {
  4.     TextInput({ placeholder: '请输入用户名', text: this.account })
  5.       .onChange((value: string) => {
  6.         this.account = value;
  7.       })
  8.       .fontSize(18)
  9.       .fontColor(Color.Black)
  10.       .width('100%')
  11.       .height(50)
  12.       .padding({ left: 10, right: 10 });
  13.     Divider().height(1).color('#194487fe'); // 下划线
  14.   }
  15.   // 密码输入框
  16.   Column() {
  17.     TextInput({ placeholder: '请输入密码', text: this.password })
  18.       .onChange((value: string) => {
  19.         this.password = value;
  20.       })
  21.       .fontSize(18)
  22.       .fontColor(Color.Black)
  23.       .width('100%')
  24.       .height(50)
  25.       .padding({ left: 10, right: 10 })
  26.       .type(InputType.Password);
  27.     Divider().height(1).color('#194487fe'); // 下划线
  28.   }.margin({ top: 32 });
  29. }
  30. .width('100%')
  31. .padding({ left: 32, right: 32 })
  32. .backgroundColor(Color.White);
复制代码


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

    • TextInput 是一个输入框组件,用于用户输入文本。
    • placeholder 是输入框的占位符文本。
    • text 是输入框当前表现的文本,绑定到 this.account 和 this.password。
    • onChange 是输入框的值改变时的回调函数,用于更新 this.account 和 this.password。
    • fontSize、fontColor、width、height 和 padding 设置了输入框的样式。
    • type(InputType.Password) 将输入框范例设置为暗码输入框。

  • Divider 是一个分隔线组件,用于在输入框下方添加一条线,模拟下划线效果。
4.2.5 记住暗码与忘记暗码行

  1. Row() {
  2.   Row() {
  3.     // 根据用户是否选择记住密码,显示不同的图标
  4.     Image($r(this.rememberPassword ? 'app.media.radio_normal_checkmark' : 'app.media.radio_normal'))
  5.       .width(20)
  6.       .height(20)
  7.       .onClick(async () => {
  8.         // 切换记住密码的状态
  9.         this.rememberPassword = !this.rememberPassword;
  10.         await PreferencesUtils.put("rememberPassword", this.rememberPassword);
  11.       });
  12.     Text('记住密码')
  13.       .fontSize(14)
  14.       .fontColor(Color.Red)
  15.       .margin({ left: 10.5 })
  16.       .onClick(async () => {
  17.         // 也可以点击文字时切换记住密码的状态
  18.         this.rememberPassword = !this.rememberPassword;
  19.         await PreferencesUtils.put("rememberPassword", this.rememberPassword);
  20.         if (!this.rememberPassword) {
  21.           await PreferencesUtils.put("account", '');
  22.           await PreferencesUtils.put("password", '');
  23.         }
  24.       });
  25.   }
  26.   Text('忘记密码')
  27.     .fontSize(14)
  28.     .fontColor(Color.Gray)
  29.     .onClick(() => {
  30.       // 实现忘记密码功能,跳转或弹出窗口
  31.       this.handleForgotPassword();
  32.     });
  33. }
  34. .justifyContent(FlexAlign.SpaceBetween)
  35. .margin({ top: 21 })
  36. .padding({ left: 32, right: 32 })
  37. .width('100%');
复制代码


  • Row 是一个程度布局组件,用于将子组件程度排列。
  • 内部的 Row 包含记住暗码的图标和文本。

    • Image 是一个图像组件,根据 this.rememberPassword 的值表现差别的图标。
    • onClick 是点击事件的回调函数,用于切换记住暗码的状态并保存到偏好设置中。
    • Text('记住暗码') 表现记住暗码的文本,设置了字体巨细、颜色和左边距,并绑定了点击事件。

  • 外部的 Row 包含忘记暗码的文本,设置了字体巨细、颜色,并绑定了点击事件。
  • justifyContent(FlexAlign.SpaceBetween) 使子组件在程度方向上均匀分布。
  • margin 和 padding 设置了外边距和内边距。
  • width 设置了组件的宽度。
4.2.6 登录按钮

  1. Button('登 录')
  2.   .width('60%')
  3.   .height(50)
  4.   .backgroundColor(Color.Red)
  5.   .fontSize(18)
  6.   .fontColor(Color.White)
  7.   .onClick(() => {
  8.     // 处理登录逻辑,并且在用户选择记住密码时保存密码
  9.     this.handleLogin();
  10.   })
  11.   .margin({ top: 56 });
复制代码


  • Button 是一个按钮组件,用于触发登录操纵。
  • width 和 height 设置了按钮的宽度和高度。
  • backgroundColor 和 fontColor 设置了按钮的配景颜色和字体颜色。
  • onClick 是按钮的点击事件回调函数,用于处理处罚登录逻辑。
  • margin 设置了按钮的上边距。
4.2.7 底部图片

  1. Image($r('app.media.login_bottom'))
  2.   .width('100%');
复制代码


  • Image 是一个图像组件,用于表现底部图片。
  • width 设置了图片的宽度。
  • $r 是资源引用函数,用于引用应用中的资源。
4.2.8 加载进度条(可选)

  1. if (this.loading) {
  2.   LoadingProgress().height(180).color('#cd0401');
  3. }
复制代码


  • LoadingProgress 是一个加载进度条组件,用于表现加载状态。
  • height 和 color 设置了进度条的高度和颜色。
  • if (this.loading) 控制加载进度条的表现,只有当 this.loading 为 true 时才会表现加载进度条。
4.3 handleLogin 方法

  1. async handleLogin() {
  2.   if (this.rememberPassword) {
  3.     // 如果用户选择了记住密码,保存账户和密码到本地存储
  4.     await PreferencesUtils.put("account", this.account);
  5.     await PreferencesUtils.put("password", this.password);
  6.   }
  7.   // 执行登录逻辑
  8.   this.loading = true;
  9.   Login<LoginResponse>({
  10.     username: this.account,
  11.     password: this.password,
  12.     uuid: '',
  13.     code: '',
  14.   }).then(async (res) => {
  15.     if (res.code !== 0) {
  16.       promptAction.showToast({
  17.         message: res.msg,
  18.         duration: 1000,
  19.       });
  20.       this.loading = false;
  21.     } else {
  22.       // 保存用户数据和 token
  23.       await PreferencesUtils.put("userData", res.data);
  24.       await PreferencesUtils.put("token", res.data.token);
  25.       // 获取权限
  26.       const permission = await GetPermission<PermissionResponse>();
  27.       await PreferencesUtils.put("permission", permission);
  28.       await PreferencesUtils.put("SystemUser_Permission", permission.data.dataPermission);
  29.       // 获取菜单权限
  30.       const menuRes = await GetMenu<PermissionResponse>();
  31.       await PreferencesUtils.put("User_Manage", menuRes.data || []);
  32.       // 跳转到主页
  33.       router.replaceUrl({ url: 'pages/MainPage' }).then(() => {
  34.         console.info('Succeeded in jumping to the pages/MainPage.');
  35.       }).catch((err: BusinessError) => {
  36.         console.error(`Failed to jump to the second page. Code is ${err.code}, message is ${err.message}`);
  37.       });
  38.       this.loading = false;
  39.     }
  40.   }).catch((err: string) => {
  41.     promptAction.showToast({
  42.       message: err,
  43.       duration: 1000,
  44.     });
  45.     this.loading = false;
  46.     console.error(`Failed to login, message is ${err}`);
  47.   });
  48. }
复制代码


  • 保存用户名和暗码:

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

  • 执行登录逻辑:

    • 设置 loading 为 true,表现加载进度。
    • 调用 Login API 举行登录,传入用户名、暗码、UUID 和验证码。
    • 乐成回调:

      • 假如 res.code 不为 0,表现错误提示。
      • 假如 res.code 为 0,保存用户数据和 token 到本地存储。
      • 调用 GetPermission API 获取权限,并保存到本地存储。
      • 调用 GetMenu API 获取菜单权限,并保存到本地存储。
      • 使用 router.replaceUrl 方法跳转到主页,并处理处罚跳转乐成和失败的情况。

    • 失败回调:

      • 表现错误提示,并设置 loading 为 false。


4.4 handleForgotPassword 方法

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


  • 跳转到忘记暗码页面:

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

5. 关键函数



  • PreferencesUtils.get 和 PreferencesUtils.put: 用于从本地存储中读取和保存数据。
  • router.getParams: 用于获取路由通报的参数。
  • router.replaceUrl 和 router.pushUrl: 用于在页面之间举行跳转。
  • promptAction.showToast: 用于表现短暂的提示信息。
7. 页面效果

进入页面:

登录接口请求中:

6. 总结

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

何小豆儿在此

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表