【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 方法
- 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: 从本地存储中获取数据,这里主要是获取用户是否记住暗码的状态,还有记录下的账户与暗码。
- 条件判定:
- 假如用户选择了记住暗码,从本地存储中读取用户名和暗码。
- 假如用户名和暗码都已存在且 路由参数stopLogin 为 false,主动调用 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: 加载进度组件,当 loading 为 true 时表现。
4.2.1 总体布局
这段代码界说了一个 build 方法,用于构建一个登录界面。界面包含以下几个主要部门:
- 顶部的接待文字
- 用户名和暗码输入框
- 记住暗码和忘记暗码选项
- 登录按钮
- 底部图片
- 加载进度条(可选)
代码界说了一个登录界面,包含接待文字、用户名和暗码输入框、记住暗码和忘记暗码选项、登录按钮和底部图片。通过使用 Stack、Column 和 Row 布局组件,以及 Text、TextInput、Image 和 Button 等 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.account 和 this.password。
- onChange 是输入框的值改变时的回调函数,用于更新 this.account 和 this.password。
- fontSize、fontColor、width、height 和 padding 设置了输入框的样式。
- 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) 使子组件在程度方向上均匀分布。
- margin 和 padding 设置了外边距和内边距。
- width 设置了组件的宽度。
4.2.6 登录按钮
- Button('登 录')
- .width('60%')
- .height(50)
- .backgroundColor(Color.Red)
- .fontSize(18)
- .fontColor(Color.White)
- .onClick(() => {
- // 处理登录逻辑,并且在用户选择记住密码时保存密码
- this.handleLogin();
- })
- .margin({ top: 56 });
复制代码
- Button 是一个按钮组件,用于触发登录操纵。
- width 和 height 设置了按钮的宽度和高度。
- backgroundColor 和 fontColor 设置了按钮的配景颜色和字体颜色。
- 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 是一个加载进度条组件,用于表现加载状态。
- height 和 color 设置了进度条的高度和颜色。
- if (this.loading) 控制加载进度条的表现,只有当 this.loading 为 true 时才会表现加载进度条。
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 方法将用户名和暗码保存到本地存储。
- 执行登录逻辑:
- 设置 loading 为 true,表现加载进度。
- 调用 Login API 举行登录,传入用户名、暗码、UUID 和验证码。
- 乐成回调:
- 假如 res.code 不为 0,表现错误提示。
- 假如 res.code 为 0,保存用户数据和 token 到本地存储。
- 调用 GetPermission API 获取权限,并保存到本地存储。
- 调用 GetMenu API 获取菜单权限,并保存到本地存储。
- 使用 router.replaceUrl 方法跳转到主页,并处理处罚跳转乐成和失败的情况。
- 失败回调:
- 表现错误提示,并设置 loading 为 false。
4.4 handleForgotPassword 方法
- handleForgotPassword() {
- // 前往忘记密码页面
- router.pushUrl({ url: 'pages/Forgot' });
- }
复制代码
- 跳转到忘记暗码页面:
- 使用 router.pushUrl 方法跳转到忘记暗码页面。
5. 关键函数
- PreferencesUtils.get 和 PreferencesUtils.put: 用于从本地存储中读取和保存数据。
- router.getParams: 用于获取路由通报的参数。
- router.replaceUrl 和 router.pushUrl: 用于在页面之间举行跳转。
- promptAction.showToast: 用于表现短暂的提示信息。
7. 页面效果
进入页面:
登录接口请求中:
6. 总结
这段代码实现了一个完备的登录页面,包罗用户名和暗码的输入、记住暗码功能、忘记暗码功能以及登录逻辑。它使用了 HarmonyOS NEXT 的组件和 API,通过状态变量管理页面的状态,通过异步方法处理处罚登录、权限获取和页面跳转等操纵。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |