鸿蒙应用开发实战【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------三步换号向导。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- openHarmony 跨平台社区:https://openharmonycrossplatform.csdn.net
- HarmonyOS 官方文档:https://developer.huawei.com/consumer/cn/doc/
- HarmonyOS ArkUI组件参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkui-ts
- HarmonyOS router API:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/router
