鸿蒙应用开发实战【41】— 登录页面LoginPage完整开发

鸿蒙应用开发实战【41】--- 登录页面LoginPage完整开发

本文是「号码助手全栈开发系列」第 41 篇,持续更新中...

开源社区:https://openharmonycrossplatform.csdn.net


前言

从本篇文章开始,我们进入 核心页面开发阶段 ,用 ArkUI 组件将前面 26-40 篇的数据模型转化为可交互的界面。第一站就是应用的"门面"------登录页面

号码助手的登录页支持两种模式:华为账号一键登录 (HMS AccountKit)和本机模拟登录(调试模式)。

本篇涵盖 :登录页 UI 布局设计、华为 HMS AccountKit 集成、@ohos.account 授权流程、登录状态管理、loading 状态与按钮防重复点击、协议链接跳转、错误处理与 HMS 未安装检测、调试模式模拟跳转。


一、UI 布局设计

1.1 页面结构

登录页采用经典的"上下留白,中间 Logo + 按钮组"布局:

复制代码
┌──────────────────────┐
│      (空白填充)        │
│                      │
│     ┌──────┐         │
│     │  号   │   Logo   │
│     └──────┘         │
│    号码助手            │
│  手机号资产管理专家     │
│                      │
│      (空白填充)        │
│                      │
│  ┌──────────────────┐│
│  │华为账号一键登录    ││
│  └──────────────────┘│
│  ┌──────────────────┐│
│  │本机模拟登录(调试) ││
│  └──────────────────┘│
│  登录即代表同意        │
│  《用户协议》与《隐私政策》│
└──────────────────────┘

1.2 页面结构代码

typescript 复制代码
@Entry
@Component
struct LoginPage {
  @State isLoggingIn: boolean = false

  build() {
    Column() {
      Blank()  // 上部留白

      // Logo 区域
      Column() {
        Text('号')
          .fontSize(AppFonts.TITLE)
          .fontWeight(AppFonts.WEIGHT_BOLD)
          .fontColor('#FFFFFF')
      }
      .width(AppDimensions.AVATAR_LARGE)
      .height(AppDimensions.AVATAR_LARGE)
      .borderRadius(AppDimensions.RADIUS_XLARGE)
      .linearGradient({
        angle: 135,
        colors: [[AppColors.PRIMARY, 0], [AppColors.PRIMARY_2, 1]]
      })
      .shadow({ radius: 24, color: '#334F7CFF', offsetY: 12 })

      Text('号码助手')
        .fontSize(AppFonts.TITLE)
        .fontWeight(AppFonts.WEIGHT_BOLD)
        .fontColor(AppColors.TEXT)
        .margin({ top: 20 })

      Text('手机号资产管理专家')
        .fontSize(AppFonts.SUB)
        .fontColor(AppColors.TEXT_2)
        .margin({ top: 8 })

      Blank()  // 下部留白

      // 按钮组
      Column({ space: 12 }) {
        Button('华为账号一键登录', { type: ButtonType.Capsule })
          .width('100%')
          .height(AppDimensions.BUTTON_HEIGHT_LARGE)
          .onClick(() => this.onHuaweiLoginTap())
        // ...
      }
    }
    .backgroundColor(AppColors.BG)
  }
}

1.3 设计要点

  • Blank():自动填充剩余空间,实现上下居中效果
  • linearGradient:Logo 使用品牌渐变色,提升品质感
  • shadow:Logo 下方添加阴影,增加层次感
  • ButtonType.Capsule:胶囊按钮,符合移动端设计语言

二、华为账号一键登录

2.1 导入 AccountKit

typescript 复制代码
import { authentication } from '@kit.AccountKit';

module.json5 中需要添加权限声明:

json 复制代码
{
  "requestPermissions": [
    {
      "name": "ohos.permission.GET_ACCOUNT_HUAWEI_ID",
      "reason": "$string:login_account_reason"
    }
  ]
}

2.2 登录流程

typescript 复制代码
private async onHuaweiLoginTap(): Promise<void> {
  if (this.isLoggingIn) return  // 防重复点击
  this.isLoggingIn = true

  try {
    // 1. 构建授权请求
    const request = new authentication.HuaweiIDProvider()
      .createAuthorizationWithHuaweiIDRequest()
    request.scopes = ['profile', 'openid']
    request.forceAuthorization = false

    // 2. 执行授权
    const controller = new authentication.AuthenticationController()
    const response = await controller.executeRequest(request)

    // 3. 获取 openID
    const openID = response.data?.openID ?? ''
    if (openID) {
      AppStorage.setOrCreate<string>('huawei_open_id', openID)
      hilog.info(0x0000, TAG, '华为账号登录成功')
    }

    // 4. 跳转首页
    this.isLoggingIn = false
    this.getUIContext().getRouter().pushUrl({ url: 'pages/HomePage' })
  } catch (err) {
    this.isLoggingIn = false
    const e = err as BusinessError
    // 用户取消登录,不提示
    if (e.code === 1001502001) return
    // HMS 未安装检测
    if (e.message?.includes('not installed') || e.message?.includes('AGC')) {
      promptAction.showToast({ message: '请确保已安装华为账号应用并完成 AGC 配置' })
    } else {
      promptAction.showToast({ message: `登录失败:${e.message || '请重试'}` })
    }
  }
}

2.3 关键参数说明

参数 说明
scope 请求的权限范围,profile 获取基本资料,openid 获取唯一标识
forceAuthorization 是否强制重新授权,设为 false 可复用之前授权
openID 华为账号在应用内的唯一标识,不是华为账号 ID
code 1001502001 用户主动取消授权,不应作为错误提示

2.4 openID 用途

登录成功后,将 openID 存入 AppStorage

typescript 复制代码
AppStorage.setOrCreate<string>('huawei_open_id', openID)

后续在其他页面可以通过 @StorageProp('huawei_open_id') 读取。在云端同步场景中,openID 用作多设备间的身份标识。


三、登录按钮防重复点击

用户快速连点时,可能重复触发多次登录请求。使用 标志位 来控制:

typescript 复制代码
@State isLoggingIn: boolean = false

private async onHuaweiLoginTap(): Promise<void> {
  if (this.isLoggingIn) return  // 正在登录中,忽略点击
  this.isLoggingIn = true
  // ... 登录逻辑 ...
  this.isLoggingIn = false  // 无论成败都要重置
}

同时界面上按钮文案也同步反馈:

typescript 复制代码
Button(this.isLoggingIn ? '登录中...' : '华为账号一键登录', { type: ButtonType.Capsule })

四、调试模式:模拟登录

在开发调试阶段,每次都用 HMS 登录效率很低。号码助手提供了本机模拟登录模式:

typescript 复制代码
private loginTimer: number = -1

aboutToDisappear(): void {
  if (this.loginTimer !== -1) {
    clearTimeout(this.loginTimer)
    this.loginTimer = -1
  }
}

private onMockLoginTap(): void {
  if (this.isLoggingIn) return
  this.isLoggingIn = true
  this.loginTimer = setTimeout(() => {
    this.isLoggingIn = false
    this.loginTimer = -1
    this.getUIContext().getRouter().pushUrl({ url: 'pages/HomePage' })
  }, 600)  // 600ms 模拟加载效果
}

注意 aboutToDisappear 中清理 Timer,防止组件销毁后定时器仍执行跳转而造成内存泄漏。

特性 华为账号登录 模拟登录
适用场景 生产环境 调试开发
依赖 HMS AccountKit 无需外部依赖
用户感知 弹出授权窗口 600ms 模拟延迟
数据真实性 真实 openID 固定测试 ID

五、协议链接

5.1 行内样式

使用 Row 包裹多个 Text 组件实现"in-line"文字链接:

typescript 复制代码
Row() {
  Text('登录即代表同意 ')
    .fontSize(AppFonts.META)
    .fontColor(AppColors.TEXT_3)
  Text('《用户协议》')
    .fontSize(AppFonts.META)
    .fontColor(AppColors.PRIMARY)
    .onClick(() => {
      this.getUIContext().getRouter().pushUrl({
        url: 'pages/PrivacyPage',
        params: { type: 'terms' }
      })
    })
  Text(' 与 ')
    .fontSize(AppFonts.META)
    .fontColor(AppColors.TEXT_3)
  Text('《隐私政策》')
    .fontSize(AppFonts.META)
    .fontColor(AppColors.PRIMARY)
    .onClick(() => {
      this.getUIContext().getRouter().pushUrl({
        url: 'pages/PrivacyPage',
        params: { type: 'privacy' }
      })
    })
}

5.2 跳转方式

这里使用 getUIContext().getRouter() 而非 router.pushUrl(),因为 getUIContext() 获取的是当前组件的 UI 上下文,包含页面路由栈引用,在 @Entry 组件内行为一致。


六、入口:Application 与 Ability

EntryAbility.onWindowStageCreate 中加载登录页作为入口:

typescript 复制代码
onWindowStageCreate(windowStage: window.WindowStage): void {
  // 初始化数据库
  DatabaseService.getInstance().init(this.context).then(() => {
    hilog.info(0x0000, TAG, 'RDB ready')
  })

  // 加载登录页
  windowStage.loadContent('pages/LoginPage', (err) => {
    if (err.code) {
      hilog.error(0x0000, TAG, 'Failed to load content: %{public}s', JSON.stringify(err))
    }
  })
}

七、完整状态流转

复制代码
登录页 (LoginPage)
    │
    ├── 华为账号登录成功 → HomePage
    │
    ├── 模拟登录(调试)  → HomePage
    │
    ├── 用户取消        → 留在登录页
    │
    └── 登录失败        → Toast 提示,留在登录页

小结

要点 说明
布局设计 Column + Blank 实现上下留白居中
HMS 登录 authentication.HuaweiIDProvider 授权流程
防重复 isLoggingIn 标志位 + 按钮文案反馈
Timer 安全 aboutToDisappear 清理定时器
协议链接 Row 内联多个 Text 实现文字链接
入口加载 windowStage.loadContent('pages/LoginPage')

下一篇文章,我们开发新手引导页 WizardPage------三步换号向导。


如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

相关推荐
zt1985q1 小时前
本地部署开源轻量级监控平台 Beszel 并实现外部访问
运维·服务器·网络协议·开源
2501_918582371 小时前
鸿蒙应用开发实战【31】— CardDao 卡号增删改查完整实现
华为·harmonyos·鸿蒙系统
全堆鸿蒙1 小时前
鸿蒙应用开发实战【36】— 数据查询优化:predicates 链式调用
数据库·harmonyos·鸿蒙系统
L-影1 小时前
Server-Sent Events (SSE):让服务器“主动开口”的实时推送技术
运维·服务器·sse
特立独行的猫a1 小时前
HarmonyOS鸿蒙PC三方库移植:Node.js 三方库鸿蒙 PC 移植指南
华为·node.js·node·harmonyos·三方库移植·鸿蒙pc
心中有国也有家1 小时前
AtomGit Flutter 鸿蒙客户端:错误处理与优雅降级策略
android·javascript·flutter·华为·harmonyos
不能跑的代码不是好代码10 小时前
Linux系统常用命令中文速查表
linux·运维·服务器
youtootech10 小时前
HarmonyOS《柚兔学伴》项目实战09-待办列表 UI——List+ForEach+滑动删除
数据结构·ui·华为·list·harmonyos
不羁的木木10 小时前
HarmonyOS APP实战-基于Image Kit的图像处理APP - 第6篇:图片滤镜效果实现
图像处理·深度学习·harmonyos