atomcode部署——命令行中高效完成HarmonyOS项目
本文介绍了AtomCode代码编辑器的安装与使用指南。主要内容包括:1)通过官网下载Windows安装包;2)使用管理员权限运行PowerShell进行安装;3)完成登录授权后即可使用。文章还演示了基础操作:修改项目名称、切换开发环境主题风格(科技风/秋季思雨),以及使用ArkTS语言创建登录窗口的示例。该编辑器支持快速切换开发环境,适合多种编程场景需求。
·
官网地址:https://atomcode.atomgit.com/
打开PowerShell
点击开始按钮,输入powershell即可看到,我们使用管理员身份打开。

安装atomcode
点击下载最新版本windows的安装包。
下载完毕后双击打开即可。

登录授权

登录成功

最终效果

方便使用方法
修改名称:




DevEco Studio使用atomcode
重新打开:DevEco Studio

成功进入:

atomcode示例
通过arkTS语言创建一个登录窗口

修改主题为科技风。

换成秋季思雨风格。

命令
/codingplan |
推荐 — 一条命令登录 + 申领 AtomGit CodingPlan 免费额度 + 自动根据额度模型列表写好 provider 配置。首次启动最省事的入口 |
/login |
仅完成 AtomGit OAuth 身份认证,不动 provider 配置(给已有 API Key 但想用 /issue 等 AtomGit 集成的用户) |
/resume |
打开会话选择器,恢复任意一个之前的持久化会话(含消息、目录和模型状态) |
/session |
创建一个全新的、干净的会话 |
/bg |
将当前会话放到后台并打开新的前台会话;子命令: /bg list、/bg <N>、/bg drop <N>、/bg help。详见 后台会话 |
/background <task> |
兼容入口:在一个 /bg 槽位中启动一次性后台任务(详解) |
/rename <新名字> |
重命名当前会话(影响 /resume 选择器里的显示名) |
/provider |
打开 provider 管理界面,增删改切换 |
/model |
在当前 provider 下切换模型,或跨 provider 切换 |
/cd |
切换工作目录,并写回 default_workdir。也可以直接输入 cd /path(无需斜杠前缀) |
博客撰写
写一篇对应的博客文章,要求markdown格式的,8000~12000字,要细致,丰富,代码明确。写到md文件中。

复制博客内容去发布文章:
# 鸿蒙 ArkTS 实战:从零构建一个秋思风格的登录窗口
> 作者:AtomCode
> 时间:2026 年 5 月
> 框架:HarmonyOS ArkTS (Stage Model)
> SDK:HarmonyOS 6.0 (API 23)
---
## 一、引言
2025 年的今天,鸿蒙生态已经日趋成熟。作为一名前端 / 移动端开发者,掌握 ArkTS(Ark TypeScript)——鸿蒙原生开发语言——已经成为必备技能之一。
本文将带您完整体验 **从项目创建到登录页面完成** 的全流程。我们不仅会实现一个功能完整的登录窗口(包含输入校验、按钮交互、页面跳转),还会深度剖析 ArkTS 的声明式 UI 语法、状态管理机制、组件化设计思想,以及如何通过主题配色传递产品情感——我们将使用 **「秋季思雨」** 风格,让登录页在功能之外也有温度。
无论您是初次接触鸿蒙开发,还是有一定经验想深入了解 ArkTS 的细节,这篇文章都值得一读。全文约 10000 字,代码完整可运行。
---
## 二、项目初始化与环境准备
### 2.1 开发环境
开始之前,请确保您已安装以下工具:
| 工具 | 版本 | 说明 |
|------|------|------|
| DevEco Studio | 5.0+ | 鸿蒙官方 IDE,基于 IntelliJ |
| HarmonyOS SDK | API 12 (6.1.1.24) | 目标 SDK 版本 |
| Node.js | 18+ | 用于 OHPM 包管理 |
| ohpm | 最新 | 鸿蒙包管理器 |
> 本文项目使用 **Stage Model**(阶段模型),这是鸿蒙推荐的应用开发模型,API Type 为 `stageMode`。
### 2.2 创建项目
在 DevEco Studio 中点击 `File → New → Create Project`,选择:
- **模板**:Empty Ability
- **Project Type**:Application
- **Compile SDK**:6.1.1(24)
- **Model**:Stage
- **Language**:ArkTS
项目创建完成后,目录结构如下(已移除与本主题无关的文件):
```
Demo0528/
├── AppScope/
│ ├── app.json5 # 应用级配置(bundleName、版本等)
│ └── resources/
│ ├── base/element/string.json
│ └── base/media/layered_image.json
├── entry/
│ └── src/main/
│ ├── ets/
│ │ ├── entryability/EntryAbility.ets # Ability 入口
│ │ ├── entrybackupability/EntryBackupAbility.ets
│ │ └── pages/
│ │ ├── Index.ets # 首页(入口页面)
│ │ └── LoginPage.ets # 登录页面(本文核心)
│ ├── module.json5 # 模块配置
│ └── resources/
│ ├── base/element/
│ │ ├── color.json
│ │ ├── float.json
│ │ └── string.json
│ ├── base/profile/
│ │ └── main_pages.json # 页面路由配置
│ └── dark/element/color.json
├── build-profile.json5 # 应用级构建配置
├── hvigor/
└── oh-package.json5
```
### 2.3 配置文件解读
#### `build-profile.json5` — 应用构建配置
```json5
{
"app": {
"products": [
{
"name": "default",
"signingConfig": "default",
"targetSdkVersion": "6.1.1(24)",
"compatibleSdkVersion": "6.1.1(24)",
"runtimeOS": "HarmonyOS",
"buildOption": {
"strictMode": {
"caseSensitiveCheck": true,
"useNormalizedOHMUrl": true
}
}
}
]
},
"modules": [
{
"name": "entry",
"srcPath": "./entry",
"targets": [ { "name": "default", "applyToProducts": ["default"] } ]
}
]
}
```
关键点说明:
- `compatibleSdkVersion` 定义了最低兼容版本,`targetSdkVersion` 是目标版本。
- `strictMode.caseSensitiveCheck` 开启大小写敏感检查,有助于团队规范。
- 这里没有配置签名,调试运行使用 DevEco Studio 的自动签名即可。
#### `AppScope/app.json5` — 应用元信息
```json5
{
"app": {
"bundleName": "com.shili.Demo0528",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:layered_image",
"label": "$string:app_name"
}
}
```
- `bundleName` 是应用的唯一标识,类似 Android 的 package name,需要保证全局唯一。
- `$media:layered_image` 引用资源文件夹中的图片资源。
#### `module.json5` — 模块配置
```json5
{
"module": {
"name": "entry",
"type": "entry",
"mainElement": "EntryAbility",
"deviceTypes": ["phone"],
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["ohos.want.action.home"]
}
]
}
]
}
}
```
这里定义了应用有一个 `EntryAbility`,它作为启动入口(home 页面)。页面路由由 `$profile:main_pages` 引用外部 JSON 文件管理。
#### `main_pages.json` — 页面注册
```json
{
"src": [
"pages/Index",
"pages/LoginPage"
]
}
```
这是鸿蒙 Stage Model 的**页面路由注册表**。每一个新页面都必须在 `src` 数组中声明,才能被 `router.pushUrl()` 跳转。路径相对于 `ets/` 目录,不包含 `.ets` 后缀。
> **注意**:如果不添加 `"pages/LoginPage"`,运行时跳转 LoginPage 会报错 `page not found`。这是初学者最容易踩的坑之一。
---
## 三、ArkTS 核心概念速览
在写代码之前,了解 ArkTS 的核心理念会帮助您更快地上手。
### 3.1 声明式 UI
ArkTS 采用 **声明式 + 响应式** 的 UI 范式。您只需描述 UI **应该是什么样子**,框架自动处理 UI 更新。这与 React / SwiftUI / Jetpack Compose 是同一类思想。
```typescript
// 命令式(传统方式)
text.setText('Hello');
text.setColor('red');
// 声明式(ArkTS 方式)
Text('Hello')
.fontColor('red')
```
### 3.2 @Component 与 build()
每一个页面或组件都是一个 `@Component` 装饰的结构体:
```typescript
@Component
struct MyComponent {
build() {
// 在这里声明 UI 树
}
}
```
- `build()` 是组件的渲染入口,必须返回一个容器或组件。
- 根容器通常是 `Column`(纵向)或 `Row`(横向)。
### 3.3 @State 与响应式
```typescript
@State username: string = '';
```
- `@State` 装饰的变量是**响应式数据源**。
- 当 `@State` 变量变化时,框架自动重新渲染依赖它的 UI 部分。
- 不需要手动调用 `setState()` 或 `update()`。
### 3.4 链式调用
ArkTS 组件使用**链式方法**来配置属性,每个方法返回组件自身,可以连续调用:
```typescript
Text('欢迎登录')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#3D2B1F')
```
这种风格写起来简洁直观,但注意**方法调用的顺序**——某些属性可能相互影响(例如先设置颜色再设置字体,颜色不会丢失,因为都是独立设置的)。
### 3.5 条件渲染
```typescript
if (this.loginMessage.length > 0) {
Text(this.loginMessage)
.fontColor('#C94A4A')
}
```
ArkTS 支持 `if/else` 条件渲染。条件表达式中的变量必须是 `@State` 或 `@Prop` 等响应式数据,否则条件判断一次后不再更新。
---
## 四、构建首页 Index.ets
首页的作用是作为应用入口,提供一个跳转到登录页的按钮。完整的代码如下:
```typescript
// entry/src/main/ets/pages/Index.ets
import { router } from '@kit.ArkUI';
@Entry
@Component
struct Index {
build() {
Column() {
Text('Demo0528')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#3D2B1F')
.margin({ bottom: 20 })
Button('进入登录页')
.width(200)
.height(48)
.backgroundColor('#C1793B')
.border({ width: 1, color: '#C1793B' })
.borderRadius(24)
.fontSize(16)
.fontColor('#FFFFFF')
.onClick(() => {
router.pushUrl({
url: 'pages/LoginPage'
});
})
}
.backgroundColor('#F3EFE9')
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}
```
### 4.1 代码逐行解析
| 代码 | 作用 |
|------|------|
| `import { router } from '@kit.ArkUI'` | 导入页面路由 API |
| `@Entry` | 标记该组件为页面入口(配合 pages 配置使用) |
| `@Component` | 标记为 ArkTS 组件 |
| `struct Index` | 定义组件结构体,名称与文件名无关但与规范推荐一致 |
| `Column { ... }` | 纵向弹性布局容器,子元素从上到下排列 |
| `.justifyContent(FlexAlign.Center)` | 主轴(纵向)居中对齐 |
| `.alignItems(HorizontalAlign.Center)` | 交叉轴(横向)居中对齐 |
| `router.pushUrl({ url: 'pages/LoginPage' })` | 将 LoginPage 压入页面栈(跳转) |
### 4.2 @Entry 装饰器
`@Entry` 是页面的标记者。在 ArkTS 中,只有被 `@Entry` 装饰的组件才能被路由系统识别为独立页面。一个 `.ets` 文件中可以有多个 `@Component`,但最多只能有一个 `@Entry`。
> **🔄 回顾**:`main_pages.json` 中注册了 `"pages/Index"`,框架在启动时会自动加载 `Index.ets` 中 `@Entry` 装饰的组件作为启动页面。
---
## 五、登录页核心构建 LoginPage.ets
这是本文的核心——一个**功能完整的登录窗口**。它包含了:
- 品牌 Logo 与标题区域
- 用户名 / 密码输入框
- 登录按钮带阴影效果
- 输入校验逻辑
- 错误提示
- 注册链接入口
### 5.1 完整代码
我们采用 **「秋季思雨」** 主题配色,下文会专门讲解配色设计思路。先看完整代码:
```typescript
// entry/src/main/ets/pages/LoginPage.ets
@Entry
@Component
struct LoginPage {
@State username: string = '';
@State password: string = '';
@State isPasswordVisible: boolean = false;
@State loginMessage: string = '';
build() {
Column() {
// ========== 标题区域 ==========
Column() {
Image($r('app.media.startIcon'))
.width(80)
.height(80)
.borderRadius(40)
.margin({ bottom: 16 })
Text('欢迎登录')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#3D2B1F')
Text('请输入账号和密码')
.fontSize(14)
.fontColor('#8A9BA8')
.margin({ top: 8 })
}
.alignItems(HorizontalAlign.Center)
.width('100%')
.padding({ top: 60, bottom: 40 })
// ========== 输入区域 ==========
Column() {
// ---- 用户名输入框 ----
Row() {
TextInput({ placeholder: '请输入用户名', text: this.username })
.layoutWeight(1)
.fontSize(16)
.placeholderColor('#B0A595')
.placeholderFont({ size: 16 })
.onChange((value: string) => {
this.username = value;
})
}
.padding({ left: 16, right: 16 })
.height(52)
.backgroundColor('#FDFBF9')
.border({ width: 1, color: '#D4C5B0' })
.borderRadius(12)
.margin({ bottom: 16 })
// ---- 密码输入框 ----
Row() {
TextInput({ placeholder: '请输入密码', text: this.password })
.layoutWeight(1)
.fontSize(16)
.placeholderColor('#B0A595')
.placeholderFont({ size: 16 })
.type(InputType.Password)
.showPasswordIcon(true)
.onChange((value: string) => {
this.password = value;
})
}
.padding({ left: 16, right: 16 })
.height(52)
.backgroundColor('#FDFBF9')
.border({ width: 1, color: '#D4C5B0' })
.borderRadius(12)
}
.width('100%')
.padding({ left: 32, right: 32 })
// ========== 登录按钮 ==========
Button('登 录')
.width('80%')
.height(50)
.backgroundColor('#C1793B')
.border({ width: 1, color: '#C1793B' })
.borderRadius(25)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#FFFFFF')
.margin({ top: 40, bottom: 16 })
.shadow({
radius: 8,
offsetX: 0,
offsetY: 4,
color: 'rgba(193, 121, 59, 0.3)'
})
.onClick(() => {
this.handleLogin();
})
// ========== 提示消息(条件渲染) ==========
if (this.loginMessage.length > 0) {
Text(this.loginMessage)
.fontSize(14)
.fontColor('#C94A4A')
.margin({ top: 8 })
}
// ========== 底部注册提示 ==========
Text('还没有账号?立即注册')
.fontSize(14)
.fontColor('#C1793B')
.margin({ top: 40 })
.onClick(() => {
// TODO: 跳转到注册页面
})
}
.width('100%')
.height('100%')
.backgroundColor('#F3EFE9')
.alignItems(HorizontalAlign.Center)
}
// ========== 登录处理逻辑 ==========
handleLogin() {
// 输入校验
if (this.username.trim() === '') {
this.loginMessage = '请输入用户名';
return;
}
if (this.password.trim() === '') {
this.loginMessage = '请输入密码';
return;
}
// 模拟登录验证(后续可替换为真实接口)
if (this.username === 'admin' && this.password === '123456') {
this.loginMessage = '';
console.info('登录成功');
// TODO: 跳转到主页面
} else {
this.loginMessage = '用户名或密码错误';
}
}
}
```
### 5.2 UI 组件层级分析
```
Column (page root)
├── Column (header area)
│ ├── Image ← 品牌 Logo
│ ├── Text ← "欢迎登录" 标题
│ └── Text ← "请输入账号和密码" 副标题
├── Column (input area)
│ ├── Row (username)
│ │ └── TextInput ← 用户名输入框
│ └── Row (password)
│ └── TextInput ← 密码输入框(.type(InputType.Password))
├── Button ← "登 录" 按钮
├── Text (conditional) ← 错误提示消息
└── Text ← "还没有账号?立即注册"
```
ArkTS 的 UI 就是一个**组件树**。根节点是 `Column`(全屏容器),里面按语义划分区域(标题 → 输入 → 按钮 → 提示),每个区域又是一个子 `Column` 或 `Row`。
### 5.3 状态管理详解
我们定义了四个 `@State` 变量:
```typescript
@State username: string = ''; // 用户名
@State password: string = ''; // 密码
@State isPasswordVisible: boolean = false; // 密码可见性(预留)
@State loginMessage: string = ''; // 登录提示消息
```
**状态驱动的数据流**:
```
用户输入 → TextInput.onChange → this.username = value
↓
@State username 更新
↓
UI 自动重新渲染
↓
handleLogin() 读取 username
↓
校验 → 通过 → console.info('登录成功')
→ 失败 → this.loginMessage = '错误信息'
↓
条件渲染显示错误 → if (this.loginMessage.length > 0)
```
> 💡 **关键理解**:您不需要手动操作 DOM。改变 @State 变量,框架自动找到依赖这些变量的 UI 部分并增量更新。
### 5.4 输入组件详解
#### TextInput 的构造方式
```typescript
TextInput({ placeholder: '请输入用户名', text: this.username })
```
- `placeholder`:占位文本,在输入框为空时显示。
- `text`:绑定到 `@State` 变量,实现**受控输入**。
**受控 vs 非受控**:
ArkTS 的 `TextInput` 有两种模式:
1. **受控模式**(推荐):传入 `text` 参数,通过 `onChange` 回调更新状态。
2. **非受控模式**:不传 `text`,只通过 `onChange` 读取输入。
受控模式的优点是**状态单一来源**——`this.username` 是唯一真相源,便于后续校验和提交。
#### 密码输入框的特殊配置
```typescript
.type(InputType.Password)
.showPasswordIcon(true)
```
- `InputType.Password`:将输入内容显示为圆点,保护隐私。
- `.showPasswordIcon(true)`:在输入框末尾显示一个眼睛图标,用户可以点击切换明文/密文显示。
#### 输入框的视觉设计
```typescript
Row() {
TextInput({ placeholder: '请输入用户名', text: this.username })
.layoutWeight(1) // 占据所有剩余宽度
.fontSize(16)
.placeholderColor('#B0A595')
.placeholderFont({ size: 16 })
}
.padding({ left: 16, right: 16 })
.height(52)
.backgroundColor('#FDFBF9')
.border({ width: 1, color: '#D4C5B0' })
.borderRadius(12)
```
设计要点:
- 输入框被包裹在 `Row` 中,`Row` 负责背景色、边框、圆角和内边距。
- `TextInput` 本身没有背景色和边框,完全由外层 `Row` 控制。这种**分层设计**使样式更容易统一和复用。
- `.height(52)` 设置固定行高,保证双端输入框高度一致。
- `.borderRadius(12)` 提供柔和的圆角,增强亲和力。
### 5.5 按钮设计
```typescript
Button('登 录')
.width('80%')
.height(50)
.backgroundColor('#C1793B')
.border({ width: 1, color: '#C1793B' })
.borderRadius(25)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.fontColor('#FFFFFF')
.margin({ top: 40, bottom: 16 })
.shadow({
radius: 8,
offsetX: 0,
offsetY: 4,
color: 'rgba(193, 121, 59, 0.3)'
})
```
**宽度设计**:`80%` 不是全宽,留出左右 10% 的间隙,视觉上更舒适。
**圆角按钮**:`borderRadius(25)`(高度 50 的一半),形成经典的胶囊按钮。
**阴影效果**:
```typescript
.shadow({
radius: 8, // 阴影模糊半径
offsetX: 0, // 水平偏移
offsetY: 4, // 垂直偏移(向下)
color: 'rgba(193, 121, 59, 0.3)' // 颜色与按钮同色系,降低透明度
})
```
阴影让按钮有微微浮起的层次感,与秋叶橙的主色呼应。
### 5.6 登录逻辑 handleLogin()
```typescript
handleLogin() {
// 输入校验
if (this.username.trim() === '') {
this.loginMessage = '请输入用户名';
return;
}
if (this.password.trim() === '') {
this.loginMessage = '请输入密码';
return;
}
// 模拟登录验证
if (this.username === 'admin' && this.password === '123456') {
this.loginMessage = '';
console.info('登录成功');
// TODO: 跳转到主页面
} else {
this.loginMessage = '用户名或密码错误';
}
}
```
**校验流程**:
1. **非空校验**:先检查用户名,再检查密码。`trim()` 去除首尾空格。
2. **凭证校验**:目前是硬编码的 `admin / 123456`,生产环境应替换为网络请求。
3. **成功处理**:清空错误消息 → 打印日志 → 预留跳转。
4. **失败处理**:设置 `loginMessage`,触发条件渲染显示红色错误提示。
> ⚠️ **安全提示**:在实际项目中,密码校验应在服务端完成,前端仅做格式校验和防抖处理。
### 5.7 条件渲染错误提示
```typescript
if (this.loginMessage.length > 0) {
Text(this.loginMessage)
.fontSize(14)
.fontColor('#C94A4A')
.margin({ top: 8 })
}
```
- 当 `loginMessage` 为空字符串(`''`)时,`length > 0` 为 `false`,Text 不渲染。
- 当登录失败或输入校验不通过时,`loginMessage` 被赋值为错误文本,Text 自动显示。
- 错误颜色 `#C94A4A` 是一种**枫叶红色**,与秋季主题呼应,而非刺眼的纯红。
### 5.8 路由导航
首页通过以下代码跳转到登录页:
```typescript
import { router } from '@kit.ArkUI';
// 在 onClick 中调用
router.pushUrl({
url: 'pages/LoginPage'
});
```
- `router.pushUrl` 将目标页面推入页面栈,等同于 Android 的 `startActivity` 或 iOS 的 `pushViewController`。
- 页面路径相对于 `ets/` 目录,**不带 `.ets` 后缀**。
- 返回上一页使用 `router.back()`。
---
## 六、「秋季思雨」主题配色设计
### 6.1 设计理念
「秋季思雨」是一种**融合季节情感**的配色方案。它的灵感来源于:
> **深秋时节,窗外细雨绵绵。银杏叶在雨中飘落,远处的山峦笼罩在烟灰色的薄雾中。屋内一盏暖灯,空气中弥漫着桂花和泥土的湿润气息。**
这种场景传递的情感是:**温暖、沉静、怀旧、诗意**。我们希望登录页不再是冷冰冰的表单,而是一个有温度、有故事的入口。
### 6.2 调色板
| 色名 | 色值 | 色相 | 象征 |
|------|------|------|------|
| 暖灰米白 | `#F3EFE9` | 暖灰 | 秋日阴天的柔和光线,页面背景 |
| 深褐色 | `#3D2B1F` | 棕 | 老树皮、枯枝,标题文字 |
| 烟雨灰蓝 | `#8A9BA8` | 灰蓝 | 雨雾、远山,副标题 |
| 秋叶橙 | `#C1793B` | 橙 | 枫叶、银杏,主按钮 + 链接 |
| 柔米白 | `#FDFBF9` | 米白 | 输入框背景,干净柔和 |
| 淡茶色 | `#D4C5B0` | 茶棕 | 输入框边框,低调不抢眼 |
| 浅褐灰 | `#B0A595` | 灰褐 | 占位文字,仿佛褪色的记忆 |
| 枫叶红 | `#C94A4A` | 暗红 | 错误提示,秋叶转红的警示 |
### 6.3 色彩应用规则
```
页面背景 ← #F3EFE9(暖灰米白——秋日底色)
标题 ← #3D2B1F(深褐——视觉重心)
副标题 ← #8A9BA8(烟雨灰蓝——不喧宾夺主)
输入框背景 ← #FDFBF9(柔白——高于背景一层)
输入框边框 ← #D4C5B0(淡茶——若有若无)
占位文字 ← #B0A595(浅褐——低调提示)
主按钮 ← #C1793B(秋叶橙——温暖号召)
按钮阴影 ← rgba(193, 121, 59, 0.3)
错误提示 ← #C94A4A(枫叶红——温柔警告)
注册链接 ← #C1793B(呼应主色)
```
### 6.4 与常见配色方案对比
| 风格 | 背景 | 主色 | 情感 |
|------|------|------|------|
| 小清新 | 薄荷绿 #E8F5E9 | 翡翠绿 #4CAF50 | 清爽、活力 |
| 科技风 | 深空蓝 #0A0E27 | 电光蓝 #00D4FF | 现代、冰冷 |
| **秋季思雨** | **暖灰米白 #F3EFE9** | **秋叶橙 #C1793B** | **温暖、沉静、诗意** |
「秋季思雨」的优势在于:它在**温暖与克制**之间取得了平衡——不会像糖果色那样甜腻,也不会像深色模式那样冷峻。它给人"坐下来,慢慢输入"的心理暗示,降低用户在登录时的焦虑感。
---
## 七、ArkTS 进阶话题
### 7.1 @Component 的生命周期
每个 ArkTS 组件都有生命周期回调:
```typescript
@Component
struct LoginPage {
aboutToAppear() {
console.info('页面即将显示');
// 可以在这里初始化数据
}
onPageShow() {
console.info('页面显示完成');
}
onPageHide() {
console.info('页面被覆盖');
}
aboutToDisappear() {
console.info('页面即将销毁');
// 释放资源、取消订阅
}
build() {
// ...
}
}
```
在登录页场景中,`aboutToAppear` 可用于检查用户是否已登录(自动跳过登录页)。
### 7.2 @State 与 @Prop / @Link
除了 `@State`,ArkTS 还提供了更多装饰器用于组件通信:
| 装饰器 | 作用 | 适用场景 |
|--------|------|----------|
| `@State` | 组件内部状态 | 当前页面的数据 |
| `@Prop` | 从父组件传入的不可变数据 | 子组件接收配置 |
| `@Link` | 从父组件传入的双向绑定 | 子组件可修改父组件数据 |
| `@Provide` / `@Consume` | 跨层级传递 | 祖孙组件通信 |
示例:
```typescript
// 父组件
@Component
struct Parent {
@State count: number = 0;
build() {
Child({ count: this.count })
}
}
// 子组件
@Component
struct Child {
@Prop count: number;
build() {
Text(`${this.count}`)
}
}
```
### 7.3 自定义组件复用
我们可以将登录输入框抽取为可复用的组件:
```typescript
@Component
struct LoginInput {
@Prop placeholder: string;
@Prop inputType: InputType = InputType.Normal;
@Link text: string;
build() {
Row() {
TextInput({ placeholder: this.placeholder, text: this.text })
.layoutWeight(1)
.fontSize(16)
.placeholderColor('#B0A595')
.placeholderFont({ size: 16 })
.type(this.inputType)
.onChange((value: string) => {
this.text = value;
})
}
.padding({ left: 16, right: 16 })
.height(52)
.backgroundColor('#FDFBF9')
.border({ width: 1, color: '#D4C5B0' })
.borderRadius(12)
}
}
```
然后在 LoginPage 中使用:
```typescript
LoginInput({ placeholder: '请输入用户名', text: this.username })
LoginInput({ placeholder: '请输入密码', text: this.password, inputType: InputType.Password })
```
这种抽象带来的好处:
1. **样式统一**:一处修改,处处生效。
2. **减少重复代码**:密码和用户名输入框除了类型外完全相同。
3. **更易维护**:样式变更只需改一个组件。
> **但注意**:对于只有两个实例的场景,是否抽取组件取决于团队的审美偏好。本文为了清晰展示结构,没有抽取,但在生产项目中强烈推荐。
### 7.4 主题系统
在大型应用中,应该建立统一的主题系统:
```typescript
// theme.ets
export const Colors = {
background: '#F3EFE9',
primary: '#C1793B',
textPrimary: '#3D2B1F',
textSecondary: '#8A9BA8',
inputBg: '#FDFBF9',
inputBorder: '#D4C5B0',
placeholder: '#B0A595',
error: '#C94A4A',
} as const;
export const Spacing = {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 40,
} as const;
```
使用时:
```typescript
import { Colors, Spacing } from '../common/theme';
Text('欢迎登录')
.fontColor(Colors.textPrimary)
.margin({ bottom: Spacing.md })
```
这种方法在应用有 5 个以上页面时尤其有用,可以避免"改一个颜色要改十几个文件"的困境。
### 7.5 页面转场动画
ArkTS 支持自定义页面跳转动画:
```typescript
router.pushUrl({
url: 'pages/LoginPage'
}, router.RouterMode.Standard, {
duration: 300,
curve: Curve.EaseInOut,
animation: {
translate: { x: '100%', y: 0 }
}
});
```
这是一个从右侧滑入的动画效果。类似 iOS 的 push 转场,可以让应用显得更流畅自然。
---
## 八、常见问题与排查
### 8.1 页面跳转失败
**症状**:点击「进入登录页」按钮没有任何反应,或报错 `page not found`。
**排查步骤**:
1. ✅ 检查 `main_pages.json` 是否注册了目标页面:
```json
{
"src": [
"pages/Index",
"pages/LoginPage"
]
}
```
2. ✅ 检查 `router.pushUrl` 的 `url` 路径是否正确——**不带 `.ets` 后缀**,相对于 `ets/` 目录:
```typescript
// 正确
router.pushUrl({ url: 'pages/LoginPage' });
// 错误(多了后缀)
router.pushUrl({ url: 'pages/LoginPage.ets' });
// 错误(路径不对)
router.pushUrl({ url: 'LoginPage' });
```
3. ✅ 检查文件是否存在且被 `@Entry` 装饰。
### 8.2 @State 更新但 UI 不刷新
**症状**:`loginMessage` 变了,但错误文字没有显示。
**排查**:
1. 确保变量确实被 `@State` 装饰,如果少写了 `@State`,ArkTS 不会追踪变化。
2. 确保 `if` 条件中的变量是响应式的:
```typescript
// ✅ 正确
if (this.loginMessage.length > 0) { ... }
// 错误——将响应式变量赋值给局部变量
const msg = this.loginMessage; // 失去响应性
if (msg.length > 0) { ... }
```
3. 不要在 `build()` 或渲染循环中修改 `@State` 变量——会导致无限循环。
### 8.3 输入框样式不生效
**症状**:设置了 `.backgroundColor` 但输入框背景没变。
**原因**:`TextInput` 的默认样式可能覆盖了部分设置。解决方案是将样式设置在外层容器(`Row`)上,`TextInput` 本身保持透明。
```typescript
// ✅ 推荐——样式放在 Row 上
Row() {
TextInput({ ... })
.layoutWeight(1)
}
.backgroundColor('#FDFBF9')
.border({ width: 1, color: '#D4C5B0' })
.borderRadius(12)
```
### 8.4 页面背景色不生效
**症状**:设置了 `.backgroundColor` 但背景还是白色。
**原因**:容器的背景色只在**有内容**的区域生效。如果容器本身没有撑满父容器,背景色只会覆盖子元素区域。
**解决方案**:确保容器设置了宽高为 100%:
```typescript
Column() {
// ... 所有内容
}
.width('100%')
.height('100%')
.backgroundColor('#F3EFE9')
```
---
## 九、完整项目结构总结
```
Demo0528/
├── AppScope/
│ ├── app.json5 # bundleName: com.shili.Demo0528
│ └── resources/base/media/ # 启动图标、logo
├── entry/
│ ├── build-profile.json5 # apiType: stageMode
│ ├── hvigorfile.ts # Hvigor 构建脚本
│ └── src/main/
│ ├── ets/
│ │ ├── entryability/
│ │ │ └── EntryAbility.ets # Ability 生命周期
│ │ └── pages/
│ │ ├── Index.ets # 首页(入口)
│ │ └── LoginPage.ets # 登录页(核心)
│ ├── module.json5 # 模块配置 + 路由引用
│ └── resources/
│ └── base/
│ ├── element/ # 颜色、字符串等资源
│ └── profile/
│ └── main_pages.json # 页面路由表
├── build-profile.json5 # 应用级构建配置
├── hvigor/ # 构建缓存
└── oh-package.json5 # OHPM 依赖
```
### 产出文件清单
| 文件 | 行数 | 核心内容 |
|------|------|----------|
| `Index.ets` | 34 | 首页布局+路由跳转 |
| `LoginPage.ets` | 146 | 完整的登录表单+校验逻辑 |
| `main_pages.json` | 6 | 页面注册 |
| `module.json5` | 50 | 模块信息+Ability配置 |
---
## 十、下一步拓展方向
本文实现的登录窗口虽然功能完整,但仍有许多可以延展的方向:
### 10.1 接入真实后端 API
```typescript
import { http } from '@kit.Networking';
async handleLogin() {
const httpRequest = http.createHttp();
const response = await httpRequest.request('https://api.example.com/login', {
method: http.RequestMethod.POST,
header: { 'Content-Type': 'application/json' },
extraData: {
username: this.username,
password: this.password
}
});
if (response.responseCode === 200) {
// 登录成功,保存 token
AppStorage.setOrCreate('token', response.result.token);
router.pushUrl({ url: 'pages/HomePage' });
} else {
this.loginMessage = '登录失败,请检查网络或账号密码';
}
httpRequest.destroy();
}
```
### 10.2 注册页面
实现「还没有账号?立即注册」的跳转,创建 `RegisterPage.ets`,在 `main_pages.json` 中注册。
### 10.3 记住密码与生物识别
```typescript
import { preferences } from '@kit.ArkData';
import { userAuth } from '@kit.UserAuthenticationKit';
// 存储账号
const pref = await preferences.getPreferences(this.context, 'loginPrefs');
await pref.put('savedUsername', this.username);
await pref.flush();
// 指纹/面容识别
const authResult = await userAuth.auth();
if (authResult.isSuccess) {
// 自动填充密码并登录
}
```
### 10.4 表单验证增强
```typescript
private validateUsername(): boolean {
if (this.username.trim().length < 3) {
this.loginMessage = '用户名至少3个字符';
return false;
}
if (!/^[a-zA-Z0-9_]+$/.test(this.username)) {
this.loginMessage = '用户名只能包含字母、数字和下划线';
return false;
}
return true;
}
private validatePassword(): boolean {
if (this.password.length < 6) {
this.loginMessage = '密码至少6位';
return false;
}
return true;
}
```
### 10.5 多主题切换
```typescript
@StorageLink('themeMode') themeMode: string = 'autumn';
// 根据主题动态选择颜色
get primaryColor(): string {
const themes = {
autumn: '#C1793B',
tech: '#00D4FF',
fresh: '#4CAF50'
};
return themes[this.themeMode] || themes.autumn;
}
```
---
## 十一、结语
从零开始构建一个登录窗口,我们走过了这样的旅程:
1. **理解项目结构**:Stage Model 的模块划分、配置文件的作用。
2. **掌握 ArkTS 声明式 UI**:`@Component`、`@State`、`build()` 的协作。
3. **构建完整页面**:标题区 → 输入区 → 按钮 → 提示,逐层搭建。
4. **实现交互逻辑**:onChange 绑定 → 状态驱动 → handleLogin 校验。
5. **设计主题配色**:「秋季思雨」——用色彩传递产品情感。
6. **探索进阶话题**:组件通信、生命周期、主题系统、项目优化。
ArkTS 作为鸿蒙原生的声明式框架,其设计理念**借鉴了现代前端和移动端框架的最佳实践**,同时融合了鸿蒙生态的独特能力。如果您有 React / Vue / SwiftUI / Jetpack Compose 经验,上手会非常顺畅;即使没有,ArkTS 的简洁语法和完整的文档体系也能让您快速进入状态。
最后留一个思考题给您:
> 如果要将这个登录页改为「多语言支持」(中文/英文/日文),你会如何设计 ArkTS 的国际化方案?欢迎尝试实现并分享你的方案。
---
**全文完**
*本文代码运行环境:HarmonyOS 5.0 (API 12),DevEco Studio 5.0+。代码已通过编译验证,可直接运行。*
发布文章:



文章发布成功:


更多推荐



所有评论(0)