AI租房避坑清单 —— 基于HarmonyOS原生AI应用的全流程开发实践
AI租房避坑清单 —— 基于HarmonyOS原生AI应用的全流程开发实践
摘要:本文以"AI租房避坑清单"应用的完整开发过程为蓝本,深入剖析在 HarmonyOS 平台上使用 ArkTS/ArkUI 进行原生 AI 应用开发的全流程技术实践。文章严格遵循 Align(对齐)、Architect(架构)、Atomize(原子化)、Approve(审批)、Automate(自动化)、Assess(评估)六阶段方法论,从需求分析、架构设计、代码实现到部署运维逐层递进,完整呈现了一个面向租房场景的智能避坑指南应用的诞生全过程。文中包含大量可运行的 ArkTS 代码示例、ArkUI 声明式 UI 的最佳实践以及 HarmonyOS 开发中常见问题的解决方案。本文适合 HarmonyOS 应用开发者、ArkTS 初学者以及移动端 AI 应用产品经理阅读参考。

1. 对齐阶段(Align)
对齐阶段的目标是将模糊的原始需求转化为精确的、可执行的规范文档。这一阶段的核心产出是需求确认文档和验收标准,是整个开发流程的基石。
1.1 项目上下文分析
1.1.1 技术栈全景
"AI租房避坑清单"应用构建在 HarmonyOS 生态之上,其核心技术栈如下:
| 技术维度 | 选型方案 | 版本/规格 |
|---|---|---|
| 操作系统 | HarmonyOS | API 6.0.1 (21) |
| 开发语言 | ArkTS(静态类型方言) | Stage Mode |
| UI框架 | ArkUI(声明式UI框架) | 组件化 + @State 驱动 |
| 开发工具 | DevEco Studio | 最新稳定版 |
| 构建系统 | Hvigor | oh-package.json5 配置 |
| 路由方案 | @kit.ArkUI router | 页面级跳转 |
| 数据模型 | 纯 ArkTS 类定义 | 零外部依赖 |
| 资源管理 | $r 资源引用 + rawfile | 国际化就绪 |
从项目根目录的 build-profile.json5 可以看到,目标 SDK 版本为 6.0.1(21),采用 stageMode 运行模式。oh-package.json5 中无任何第三方依赖,这是 HarmonyOS 原生开发的重要特点——所有能力均来自系统 Kit,开发者无需引入外部库即可完成完整的应用开发。
1.1.2 项目目录结构
"AI租房避坑清单"应用在项目中的完整路径如下:
entry/src/main/ets/apps/AI租房避坑清单/
├── AI租房避坑清单Page.ets # 视图层(View)
├── AI租房避坑清单Model.ets # 数据模型层(Model)
└── AI租房避坑清单Service.ets # 业务逻辑层(Service/ViewModel)
此外,该应用还涉及以下配置文件的修改:
entry/src/main/resources/base/profile/main_pages.json # 路由注册
entry/src/main/resources/rawfile/apps/apps.json # 应用列表配置
1.1.3 架构模式分析
整个项目采用 MVVM(Model-View-ViewModel) 架构范式,在 ArkTS 的语法约束下做了适应性调整。这种架构的核心优势在于关注点分离——UI 代码只负责渲染和交互,业务逻辑集中在 Service 层,数据模型独立定义,三者各司其职。
在 ArkTS 环境下,MVVM 的落地方式有其特殊性:
- Model 层:
AI租房避坑清单Data类,承载所有数据结构。在 ArkTS 中,类声明引入的是一种新类型而非值,因此天然适合作为数据模型。所有字段必须在类声明中立即初始化,不支持在构造函数中动态声明字段。 - View 层:
AI租房避坑清单Page结构体,使用@Component装饰器标记为 UI 组件,通过@State装饰器声明响应式状态变量,驱动 UI 自动刷新。 - Service 层:
AI租房避坑清单Service类,封装核心业务逻辑,包括数据生成和处理,属于 ViewModel 的变体实现。
1.2 需求理解与确认
1.2.1 原始需求
"AI租房避坑清单"的原始需求可以概括为:用户输入租房相关的关键信息(城市、预算、租房类型),应用基于这些信息生成一份全面的租房避坑清单,包含实地看房检查项、合同要点、区域建议、费用清单、入住建议和退租策略等多个维度的内容,帮助用户在租房过程中规避常见陷阱。
1.2.2 用户输入规格
应用需要采集的用户输入包含三个核心字段:
| 字段 | 含义 | 示例值 | 输入组件 |
|---|---|---|---|
| 城市 | 租房所在城市 | 北京、上海、深圳 | TextInput |
| 预算 | 月租金预算范围 | 3000、5000-8000 | TextInput |
| 租房类型 | 房屋类型 | 整租、合租、公寓、城中村 | TextInput |
1.2.3 AI输出规格
应用生成的避坑清单包含 7 个数据维度:
| 输出字段 | 类型 | 说明 |
|---|---|---|
| viewing_checklist | string[] | 实地看房检查清单 |
| category | string | 房源分类 |
| items | string[] | 具体检查项目 |
| red_flags | string[] | 危险信号/警示项 |
| contract_points | string[] | 合同审查要点 |
| point | string | 核心关注点 |
| why | string | 为什么重要 |
| negotiable | string | 是否可协商 |
| area_advice | string[] | 区域选择建议 |
| area | string | 推荐区域 |
| pros | string | 优点 |
| cons | string | 缺点 |
| avg_price | string | 均价范围 |
| cost_checklist | string[] | 费用清单(含隐藏费用) |
| move_in_tips | string[] | 入住建议 |
| exit_strategy | string | 退租注意事项 |
1.2.4 边界确认
| 维度 | 边界定义 |
|---|---|
| 输入范围 | 城市、预算、租房类型三个文本字段 |
| 输出范围 | 7个类别共16个维度的避坑信息 |
| 设备范围 | 仅支持手机(phone)设备类型 |
| 交互方式 | 文本输入 + 按钮触发 + 结果展示 |
| 网络依赖 | 当前版本无网络依赖(Mock数据演示) |
| 数据持久化 | 无需持久化,结果仅当次展示 |
| 用户群体 | 租房人群、房产中介、房东 |
1.2.5 目标用户群体分析
- 首次租房者:刚毕业的大学生或工作调动者,对租房流程不熟悉,容易踩坑,需要全面的检查清单作为参考。
- 频繁换租者:因工作或生活需要频繁换房的人群,需要一个标准化的避坑检查流程,提高效率。
- 房产中介:专业中介可以将此工具作为服务客户的辅助手段,帮助客户做出更明智的决策。
- 房东:房东可以通过此清单了解租客的关注点,有针对性地改善房屋条件,提高出租率。
1.3 关键决策记录
决策1:数据类型选择——Record<string, Object> 替代 any
问题:ArkTS 不支持 any 和 unknown 类型,也不支持索引签名 { [key: string]: string },如何管理动态表单数据?
分析:在传统 TypeScript 中,表单数据通常使用 any 或索引签名类型来管理。但 ArkTS 明确禁止了这两种方式。在 ArkTS 的语法约束下,动态表单数据的管理需要另寻出路。
决策:使用 Record<string, Object> 类型替代 any。Record 是 ArkTS 中少数支持的泛型工具类型之一(Partial、Required、Readonly 和 Record 是例外),它允许在编译时已知的键值对映射。对于 Object 类型的值,在 Service 层通过 String() 方法进行显式类型转换。这种方案在类型安全性和灵活性之间取得了平衡。
// 使用 Record<string, Object> 管理动态表单数据
@State inputData: Record<string, Object> = {}
// 采集用户输入
TextInput({ placeholder: '请输入城市' })
.onChange((val: string) => {
this.inputData['城市'] = val
})
// Service 层使用时显式转换
let cityVal: string = String(inputData['城市'] || '')
决策2:结果数据模型使用可空类型
问题:ArkTS 不支持 undefined 作为初始值,如何表示"尚未生成结果"的状态?
分析:在 ArkTS 中,所有类字段必须在声明时初始化,不能在构造函数中动态声明。这意味着不能简单地使用 undefined 来表示"数据尚未生成"的状态。
决策:使用 T | null 联合类型,将初始值设为 null。在 UI 中通过 if (this.resultData !== null) 进行类型收窄。这种模式在 ArkTS 中被广泛使用,是模拟可选值的标准做法。
@State resultData: AI租房避坑清单Data | null = null
// 在 UI 中使用
if (this.showResult && this.resultData !== null) {
// 安全访问 resultData 的字段
Text(this.resultData.category)
}
决策3:UI 布局策略——Scroll 容器承载长内容
问题:避坑清单的输出结果可能包含大量文本和列表项,如何保证所有内容在屏幕内可滚动查看?
分析:应用需要在有限屏幕空间内展示大量信息,包括输入表单、生成按钮和完整的输出结果。如果所有内容都平铺在页面上,必然超出屏幕高度。
决策:将整个内容区域包裹在 Scroll 组件中,实现垂直滚动。输入区域和输出区域在 Column 容器中垂直排列,输出区域在点击生成按钮后动态渲染,所有内容自然地在 Scroll 中滚动。
Scroll() {
Column() {
// 输入表单区域
Column() { /* 输入控件 */ }
// 生成按钮
Button('生成避坑清单')
// 结果展示区域(条件渲染)
if (this.showResult && this.resultData !== null) {
Column() { /* 结果展示 */ }
}
}
}
.layoutWeight(1)
2. 架构阶段(Architect)
架构阶段的目标是从共识文档出发,设计出系统的整体架构、分层方案和模块间接口规范。这一阶段的核心产出是详细的设计文档。
2.1 整体架构设计
"AI租房避坑清单"应用的整体架构采用经典的三层 MVVM 模式,各层职责清晰、耦合度低:
┌─────────────────────────────────────────────────────────┐
│ View 层 (Page.ets) │
│ @Entry @Component │
│ - @State inputData: Record<string, Object> │
│ - @State resultData: AI租房避坑清单Data | null │
│ - @State showResult: boolean │
│ - build() 构建 UI 树 │
│ - 用户交互事件处理 │
├─────────────────────────────────────────────────────────┤
│ Service 层 (Service.ets) │
│ AI租房避坑清单Service │
│ - generateData(input): AI租房避坑清单Data │
│ - 调用 AI 模型/Mock 数据生成逻辑 │
├─────────────────────────────────────────────────────────┤
│ Model 层 (Model.ets) │
│ AI租房避坑清单Data │
│ - 16 个字段定义全量输出结构 │
│ - 构造函数初始化所有字段 │
└─────────────────────────────────────────────────────────┘
2.1.1 各层职责边界
Model 层(数据模型)
Model 层是整个应用的数据基石。AI租房避坑清单Data 类定义了应用输出的全部数据结构,共包含 16 个字段,涵盖了看房检查、合同审查、区域建议、费用清单、入住建议和退租策略等所有维度。
Model 层的设计要点包括:
- 所有字段必须立即初始化:在 ArkTS 中,类字段声明时必须赋值,构造函数中只能重新赋值,不能声明新字段。
- 使用数组类型存储列表数据:对于多值数据(如检查项列表、费用清单等),使用
string[]类型。 - 使用字符串类型存储标量数据:对于单值数据(如区域名称、优点、缺点等),使用
string类型。
Service 层(业务逻辑)
Service 层是 View 和 Model 之间的桥梁,负责封装核心业务逻辑。AI租房避坑清单Service 类接收原始输入,执行数据生成逻辑,返回结构化的数据模型实例。
Service 层的设计考虑:
- 无状态设计:Service 实例不持有状态,每次调用
generateData都返回新的数据实例,避免副作用。 - 输入验证:在生成数据前对输入进行合法性检查,确保关键字段非空。
- 可扩展接口:当前版本使用 Mock 数据,后续可无缝替换为真实 AI 模型调用。
View 层(UI 视图)
View 层是用户与应用交互的界面,使用 ArkUI 的声明式 UI 语法构建。AI租房避坑清单Page 组件通过 @State 装饰器声明响应式状态变量,当状态变化时,UI 自动重新渲染。
View 层的设计要点:
- 状态驱动:所有 UI 变化都由
@State变量驱动,符合 ArkUI 的声明式范式。 - 条件渲染:使用
if语句控制结果区域的显示/隐藏,未生成结果时不占用屏幕空间。 - 列表渲染:使用
ForEach循环渲染数组类型的数据,每个列表项以•符号开头。
2.2 数据流设计
应用的数据流遵循"单向数据流"原则,从用户输入到 UI 渲染的完整路径如下:
用户输入 → @State inputData 更新 →
点击按钮 → Service.generateData() →
返回 AI租房避坑清单Data 实例 →
@State resultData 赋值 →
@State showResult 设为 true →
ArkUI 自动触发 UI 重渲染 →
结果展示在页面中
这种单向数据流的设计优势在于:
- 可预测性:数据变化的方向是单一的,不会出现双向绑定导致的循环更新问题。
- 可调试性:每个数据变化点都是明确的,便于定位问题。
- 性能优化:ArkUI 的响应式系统只更新变化的部分,无需手动操作 DOM。
2.3 路由与集成设计
"AI租房避坑清单"应用作为主应用的一个子模块,需要通过路由注册和列表配置集成到主应用的导航体系中。
2.3.1 路由注册
在 main_pages.json 中注册页面路由:
{
"src": [
"pages/Index",
"apps/AI租房避坑清单/AI租房避坑清单Page"
]
}
2.3.2 应用列表配置
在 apps.json 中添加应用配置,包含图标、标题、颜色主题和页面路由信息:
{
"icon": "🏡",
"title": "AI租房避坑清单",
"subtitle": "租房避坑",
"color": "#3B82F6",
"bg": "#EFF6FF",
"border": "#BFDBFE",
"page": "apps/AI租房避坑清单/AI租房避坑清单Page",
"cat": "工作效率"
}
2.3.3 页面跳转实现
在主页 Index.ets 中,通过 router.pushUrl 实现页面跳转:
.onClick(() => {
router.pushUrl({ url: app.pageUrl })
})
在应用页面中,通过 router.back() 实现返回:
Text('← 返回')
.onClick(() => {
router.back()
})
2.4 ArkTS 语法约束下的设计考量
在架构设计阶段,必须充分考虑 ArkTS 的语法约束,这些约束直接影响代码的实现方式。
2.4.1 类的声明与使用
在 ArkTS 中,类声明引入的是一种新类型,而不是一个值。这意味着:
- 不能将类赋值给变量:
let Cls = MyClass这种写法是不允许的。 - 不能将类用作对象:不能在类上定义静态属性之外的其他属性。
- 所有字段必须在声明时初始化:不支持在构造函数中声明新字段。
// 正确的做法:所有字段在声明时初始化
export class AI租房避坑清单Data {
viewing_checklist: string[] = []
category: string = ''
items: string[] = []
// ... 其他字段
constructor() {
// 构造函数中重新赋值,而非声明新字段
this.viewing_checklist = []
this.category = ''
// ...
}
}
2.4.2 状态变量的类型约束
@State 装饰器用于声明组件的内部状态,当状态变化时,UI 自动更新。在 ArkTS 中,@State 变量的类型必须明确指定,不能使用 any。
// 正确的做法:明确指定类型
@State inputData: Record<string, Object> = {}
@State resultData: AI租房避坑清单Data | null = null
@State showResult: boolean = false
2.4.3 条件渲染与类型收窄
在 ArkTS 中,条件渲染使用 if 语句。当变量类型为联合类型(如 T | null)时,需要在条件判断中进行类型收窄。
// 类型收窄:在 if 块内,resultData 的类型被收窄为 AI租房避坑清单Data
if (this.showResult && this.resultData !== null) {
Text(this.resultData.category) // 安全访问
}
3. 原子化阶段(Atomize)
原子化阶段的目标是将架构设计阶段产出的系统方案分解为更小的、可独立执行和验证的原子任务。这种分解方式有助于并行开发、降低复杂度、提高交付质量。
3.1 任务分解
"AI租房避坑清单"应用的开发任务可以分解为以下 7 个原子任务:
任务 T1:定义数据模型(Model)
任务描述:创建 AI租房避坑清单Data 类,定义全部 16 个输出字段。
验收标准:
- 类中定义所有必需的字段,每个字段都有明确的类型
- 所有字段在声明时已初始化
- 构造函数中重新初始化所有字段
预估工时:0.5 小时
任务 T2:实现业务逻辑层(Service)
任务描述:创建 AI租房避坑清单Service 类,实现 generateData 方法,接收用户输入并返回结构化的数据。
验收标准:
- 方法签名清晰,输入为
Record<string, Object>,输出为AI租房避坑清单Data - 当前版本使用 Mock 数据,后续可扩展为真实 AI 调用
- 输入为空时提供默认值
预估工时:1 小时
任务 T3:构建页面骨架(Page - 基础结构)
任务描述:创建 AI租房避坑清单Page 组件,搭建页面的基本骨架,包括顶部导航栏、标题区域和 Scroll 容器。
验收标准:
- 页面包含顶部返回按钮和标题
- 使用 Scroll 容器包裹可滚动内容区域
- 页面背景色和布局符合设计规范
预估工时:0.5 小时
任务 T4:实现输入表单(Page - 输入区域)
任务描述:在页面中实现用户输入表单,包含城市、预算、租房类型三个输入字段和生成按钮。
验收标准:
- 三个 TextInput 组件正确绑定到 inputData 状态
- 生成按钮样式醒目,点击触发数据生成
- 表单布局清晰,标签和输入框对齐
预估工时:1 小时
任务 T5:实现结果展示(Page - 输出区域)
任务描述:实现避坑清单的结果展示区域,使用条件渲染控制显示/隐藏,使用 ForEach 渲染列表数据。
验收标准:
- 生成结果后自动显示结果区域
- 每个数据维度都有对应的标题和内容展示
- 列表数据使用
•符号逐条展示 - 标量数据使用标签 + 值的方式展示
预估工时:1.5 小时
任务 T6:注册路由和配置
任务描述:在 main_pages.json 中注册页面路由,在 apps.json 中添加应用列表配置。
验收标准:
- 路由路径正确,页面可正常跳转
- 应用列表配置完整,包含图标、标题、主题色等
- 返回功能正常
预估工时:0.5 小时
任务 T7:集成测试与验证
任务描述:验证应用从主页跳转到详情页、输入数据、生成结果、返回主页的完整流程。
验收标准:
- 所有页面跳转无异常
- 输入表单正常采集数据
- 生成结果正确展示
- 返回功能正常
- 无编译错误和运行时异常
预估工时:1 小时
3.2 任务依赖图
T1 (Model) ──→ T2 (Service) ──→ T5 (结果展示)
↑
T3 (页面骨架) ──→ T4 (输入表单) ────┘
↓
T6 (路由配置) ──→ T7 (集成测试)
T1 和 T3 可以并行开发,T2 依赖 T1,T4 依赖 T3,T5 依赖 T2 和 T4,T6 依赖 T5,T7 依赖 T6。
3.3 代码实现详解
3.3.1 T1:Model 层实现
Model 层是数据定义的核心,AI租房避坑清单Data 类的完整代码如下:
// AI租房避坑清单Model.ets
export class AI租房避坑清单Data {
viewing_checklist: string[] = []
category: string = ''
items: string[] = []
red_flags: string[] = []
contract_points: string[] = []
point: string = ''
why: string = ''
negotiable: string = ''
area_advice: string[] = []
area: string = ''
pros: string = ''
cons: string = ''
avg_price: string = ''
cost_checklist: string[] = []
move_in_tips: string[] = []
exit_strategy: string = ''
constructor() {
this.viewing_checklist = []
this.category = ''
this.items = []
this.red_flags = []
this.contract_points = []
this.point = ''
this.why = ''
this.negotiable = ''
this.area_advice = []
this.area = ''
this.pros = ''
this.cons = ''
this.avg_price = ''
this.cost_checklist = []
this.move_in_tips = []
this.exit_strategy = ''
}
}
设计要点:
- 数组类型字段:
viewing_checklist、items、red_flags、contract_points、area_advice、cost_checklist、move_in_tips使用string[]类型,用于存储多值数据。 - 字符串类型字段:
category、point、why、negotiable、area、pros、cons、avg_price、exit_strategy使用string类型,用于存储单值数据。 - 双重初始化:在声明时和构造函数中分别初始化,确保字段在任何情况下都有确定的初始值。
3.3.2 T2:Service 层实现
Service 层封装了数据生成的核心逻辑。当前版本使用 Mock 数据,后续可扩展为调用真实 AI 模型。
// AI租房避坑清单Service.ets
import { AI租房避坑清单Data } from './AI租房避坑清单Model'
export class AI租房避坑清单Service {
private model: AI租房避坑清单Data
constructor() {
this.model = new AI租房避坑清单Data()
}
// 生成AI租房避坑清单数据
generateData(input: Record<string, Object>): AI租房避坑清单Data {
let result: AI租房避坑清单Data = new AI租房避坑清单Data()
// 提取用户输入,提供默认值
let cityVal: string = String(input['城市'] || '')
let budgetVal: string = String(input['预算'] || '')
let typeVal: string = String(input['租房类型'] || '')
// Mock 数据生成逻辑
result.viewing_checklist = [
'检查墙面是否有渗水、发霉痕迹',
'测试水压和排水是否正常',
'检查门窗密封性和开关是否顺畅',
'确认空调、热水器等家电能否正常使用',
'检查电路和插座是否完好',
'观察周边环境和噪音情况',
'确认网络覆盖和信号质量',
'检查消防设施是否完备'
]
result.items = [
'房屋整体结构检查',
'水电设施测试',
'家具家电检查',
'环境卫生评估',
'安全设施确认'
]
result.red_flags = [
'⚠️ 墙面有明显裂缝或渗水痕迹',
'⚠️ 电路老化,插座松动',
'⚠️ 房屋存在甲醛超标嫌疑(新装修)',
'⚠️ 周边环境嘈杂,有施工噪音'
]
result.contract_points = [
'明确押金退还条件和期限',
'确认物业费、取暖费等由谁承担',
'明确维修责任划分',
'确认是否可以转租',
'核实房东身份和房产证'
]
result.cost_checklist = [
'月租金:' + budgetVal + '元/月',
'押金:通常为1-2个月租金',
'中介费:通常为半个月到1个月租金',
'物业费:确认是否包含在租金内',
'水电燃气费:确认单价和缴费方式',
'网络费:确认是否包含',
'取暖费/空调费:季节性费用'
]
result.move_in_tips = [
'入住前拍照留存房屋现状证据',
'更换门锁或确认密码锁安全',
'确认所有家电操作方式',
'了解垃圾投放和分类要求',
'熟悉周边超市、医院、公交站位置'
]
result.exit_strategy = '退租需提前30天书面通知房东;' +
'退租时房屋需恢复原状(正常磨损除外);' +
'押金退还一般在退租后15个工作日内完成;' +
'建议退租前与房东共同检查房屋并签字确认。'
return result
}
}
设计要点:
- 输入提取:使用
String(input['城市'] || '')提取用户输入,通过|| ''提供默认值,避免undefined被转换为"undefined"字符串。 - Mock 数据:根据真实租房场景设计了完整的检查清单和避坑建议,即使在没有 AI 模型的情况下也能提供有价值的参考信息。
- 可扩展性:
generateData方法的输入输出接口已经固定,后续只需替换内部实现即可接入真实 AI 模型。
3.3.3 T3-T5:Page 层实现
Page 层是应用的 UI 部分,包含顶部导航栏、输入表单和结果展示三个主要区域。
// AI租房避坑清单Page.ets
import { AI租房避坑清单Data } from './AI租房避坑清单Model'
import { AI租房避坑清单Service } from './AI租房避坑清单Service'
import { router } from '@kit.ArkUI'
@Entry
@Component
struct AI租房避坑清单Page {
@State inputData: Record<string, Object> = {}
@State resultData: AI租房避坑清单Data | null = null
@State showResult: boolean = false
private service: AI租房避坑清单Service = new AI租房避坑清单Service()
build() {
Column() {
// 顶部导航栏
Row() {
Text('← 返回')
.fontSize(13)
.fontColor('#1A1A1A')
.onClick(() => {
router.back()
})
Blank()
Column() {
Text('🏠 AI租房避坑清单')
.fontSize(17)
.fontWeight(FontWeight.Bold)
.fontColor('#1A1A1A')
Text('RENTAL CHECKLIST · 避坑指南')
.fontSize(9)
.fontColor('#6B7280')
.margin({ top: 2 })
}
Blank()
Text('🏠')
.fontSize(22)
}
.width('100%')
.padding({ left: 20, right: 20, top: 16, bottom: 14 })
.backgroundColor('#F3F4F6')
// 可滚动内容区域
Scroll() {
Column() {
// 输入表单区域
this.buildInputForm()
// 生成按钮
this.buildGenerateButton()
// 结果展示区域
this.buildResultSection()
}
.width('100%')
.padding({ left: 18, right: 18, bottom: 40 })
}
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#F3F4F6')
}
// 输入表单区域
@Builder
buildInputForm() {
Column() {
Text('🏠 城市')
.fontSize(11)
.fontColor('#1A1A1A')
.margin({ top: 6, bottom: 3 })
TextInput({ placeholder: '请输入城市' })
.fontSize(13)
.height(40)
.backgroundColor('#FFFFFF')
.border({ width: 1, color: '#9CA3AF' })
.padding({ left: 12, right: 12 })
.onChange((val: string) => {
this.inputData['城市'] = val
})
Text('🏠 预算')
.fontSize(11)
.fontColor('#1A1A1A')
.margin({ top: 6, bottom: 3 })
TextInput({ placeholder: '请输入预算(如:3000)' })
.fontSize(13)
.height(40)
.backgroundColor('#FFFFFF')
.border({ width: 1, color: '#9CA3AF' })
.padding({ left: 12, right: 12 })
.onChange((val: string) => {
this.inputData['预算'] = val
})
Text('🏠 租房类型')
.fontSize(11)
.fontColor('#1A1A1A')
.margin({ top: 6, bottom: 3 })
TextInput({ placeholder: '请输入租房类型(整租/合租/公寓)' })
.fontSize(13)
.height(40)
.backgroundColor('#FFFFFF')
.border({ width: 1, color: '#9CA3AF' })
.padding({ left: 12, right: 12 })
.onChange((val: string) => {
this.inputData['租房类型'] = val
})
}
.width('100%')
.padding(18)
.backgroundColor('#FFFFFF')
.border({ width: 1, color: '#D1D5DB' })
.margin({ top: 6 })
}
// 生成按钮
@Builder
buildGenerateButton() {
Button('🏠 生成避坑清单')
.width('100%')
.height(50)
.backgroundColor('#1F2937')
.borderRadius(0)
.fontColor('#FFFFFF')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ top: 18, bottom: 14 })
.onClick(() => {
this.resultData = this.service.generateData(this.inputData)
this.showResult = true
})
}
// 结果展示区域
@Builder
buildResultSection() {
if (this.showResult && this.resultData !== null) {
Column() {
Text('🏠 租房避坑清单')
.fontSize(15)
.fontWeight(FontWeight.Bold)
.fontColor('#1A1A1A')
.margin({ bottom: 12 })
//
更多推荐

所有评论(0)