AI旅行规划师 —— 基于 HarmonyOS 的 AI 应用开发全流程技术实践
AI旅行规划师 —— 基于 HarmonyOS 的 AI 应用开发全流程技术实践
引言
在当今快节奏的生活中,旅行已成为人们放松身心、探索世界的重要方式。然而,一次完美的旅行离不开周密的规划——从目的地选择、行程安排、交通预订,到住宿推荐、美食攻略、预算控制,甚至还包括避坑指南和应急准备,涉及的信息维度极为庞杂。传统的旅行规划方式往往需要用户在多个平台之间反复切换,手动收集和整理信息,耗时耗力且容易遗漏关键细节。
本文以 HarmonyOS 平台上的 “AI旅行规划师” 应用为例,详细阐述从需求对齐到最终交付的完整开发流程。我们将深入探讨 ArkTS 语言在鸿蒙生态中的最佳实践,分享声明式 UI 开发、状态管理、分层架构设计等核心技术要点的实战经验,并展示如何通过标准化的开发流程,将 AI 能力高效地融入 HarmonyOS 应用生态。

1. 对齐阶段(Align)
对齐阶段的目标是将模糊的产品需求转化为精确的技术规范。这是整个开发流程的基石,决定了后续所有工作的方向和质量。在 “AI旅行规划师” 项目中,我们通过与产品团队和设计团队的深入沟通,逐步将 “做一个 AI 旅行规划工具” 的模糊想法,转化为可执行、可验证的精确需求规范。
1.1 项目上下文分析
技术栈全景
“AI旅行规划师” 应用是 HarmonyOS 生态中的一个 AI 子应用,运行在以下技术栈之上:
- 操作系统:HarmonyOS 6.0.1(API 21,Stage 模型)
- 开发语言:ArkTS(基于 TypeScript 的鸿蒙原生语言)
- UI 框架:ArkUI 声明式 UI 框架
- 构建工具:Hvigor(鸿蒙原生构建工具)
- 目标设备:Phone(手机)
- SDK 兼容:targetSdkVersion 6.0.1(21),compatibleSdkVersion 6.0.1(21)
该项目是一个大型 AI 应用集合的一部分,整个应用市场包含多个 AI 应用,覆盖健康生活、工作效率、创意娱乐、学习成长、职业发展五大类别。“AI旅行规划师” 归属于"创意娱乐"类别,图标为 📱,副标题为"旅行海报"。
项目结构分析
entry/src/main/ets/
├── apps/
│ └── AI旅行规划师/
│ ├── AI旅行规划师Page.ets # 页面层(View)
│ ├── AI旅行规划师Model.ets # 数据模型层(Model)
│ └── AI旅行规划师Service.ets # 业务逻辑层(Service)
├── pages/
│ └── Index.ets # 主入口页面(应用列表)
├── entryability/
│ └── EntryAbility.ets # Ability 入口
└── entrybackupability/
└── EntryBackupAbility.ets # 备份扩展
架构模式分析
通过分析代码结构,可以发现该应用采用了 分层架构(Layered Architecture) 模式,类似于前端领域的 MVVM 模式。整个应用由三个核心模块组成:
- Page 层(View):负责 UI 渲染和用户交互,使用 ArkUI 的
@Component装饰器定义组件,通过@State装饰器管理状态变量 - Model 层:定义数据实体
AI旅行规划师Data,封装所有业务数据字段,包括行程安排、景点信息、餐饮推荐、交通方案、预算规划、避坑指南等 - Service 层:封装核心业务逻辑,将输入数据转换为结构化的旅行规划结果
这种分层模式在 HarmonyOS 应用开发中非常典型,体现了良好的关注点分离原则。每个层次职责清晰,Page 层只关心 UI 展示,Service 层只关心业务逻辑,Model 层只关心数据结构定义。
依赖关系分析
在 oh-package.json5 中可以看到,应用层没有额外的第三方依赖,仅依赖鸿蒙 SDK 内置的 @kit.ArkUI 和 @kit.ArkTS 框架。这表明 HarmonyOS 的 SDK 已经提供了足够丰富的原生 API 支持,无需引入外部库即可完成复杂的 UI 交互和数据展示。
1.2 需求理解确认
经过对项目代码和配置文件的全面分析,我们对 “AI旅行规划师” 的需求进行如下确认:
| 需求项 | 描述 | 验收标准 |
|---|---|---|
| 目的地输入 | 支持用户输入旅行目的地 | 输入框可正常录入文本 |
| 出行方式输入 | 支持用户选择/输入出行方式 | 输入框可正常录入文本 |
| 预算输入 | 支持用户输入旅行预算 | 输入框可正常录入文本(数字) |
| AI 规划生成 | 基于输入数据生成结构化旅行规划 | 点击"生成海报"按钮后显示规划结果 |
| 行程展示 | 展示完整的行程安排 | 结果区域展示 itinerary 列表 |
| 景点推荐 | 展示推荐景点列表 | 结果区域展示 spots 列表 |
| 餐饮推荐 | 展示早中晚餐推荐 | 结果区域展示 breakfast/lunch/dinner |
| 交通方案 | 展示出行交通方案详情 | 结果区域展示 transport 信息 |
| 住宿推荐 | 展示住宿建议 | 结果区域展示 accommodation 信息 |
| 预算明细 | 展示各项费用预算 | 结果区域展示 budget 信息 |
| 当地美食 | 展示当地特色美食推荐 | 结果区域展示 local_food 列表 |
| 避坑指南 | 展示旅行注意事项 | 结果区域展示 pitfalls 列表 |
| 应急信息 | 展示当地应急联系方式 | 结果区域展示 emergency 信息 |
| 页面导航 | 支持返回上级页面 | 点击"← 返回"可返回应用列表 |
1.3 核心痛点分析
在需求对齐过程中,我们深入分析了用户在当前旅行规划场景中面临的核心痛点:
1. 信息碎片化严重
一次完整的旅行规划往往需要横跨多个平台获取信息:在携程查机票酒店、在马蜂窝看攻略游记、在美团找美食推荐、在百度查交通路线、在小红书看避坑指南。用户需要在多个应用之间反复切换,手动汇总信息,效率极低。
2. 个性化程度不足
通用旅行攻略面向的是"大众用户",无法根据个人的预算、偏好、出行方式等因素进行定制化推荐。例如,穷游用户和高端用户的需求完全不同,但传统攻略往往无法区分。
3. 规划缺乏系统性
大多数旅行规划工具只关注单一维度(如机票比价),缺乏从行程到交通、从住宿到餐饮、从预算到应急信息的全链路覆盖。用户需要自己拼凑各个维度的信息,容易遗漏关键细节。
4. 时间成本高
据统计,规划一次为期 5 天的旅行,平均需要耗时 8-12 小时进行信息收集和整理。对于时间宝贵的现代用户而言,这是一个巨大的痛点。
5. 避坑信息获取困难
旅行中的"坑"往往是用户最关心的问题——景区门票贵不贵、当地交通是否便利、有哪些消费陷阱、天气如何准备等。但这些信息分散在各种用户评价中,难以系统化获取。
1.4 技术方案决策
在技术选型上,我们做了以下关键决策:
| 决策项 | 选择 | 理由 |
|---|---|---|
| UI框架 | ArkUI(声明式) | HarmonyOS原生框架,性能最优,与系统深度集成 |
| 编程语言 | ArkTS | HarmonyOS原生应用开发语言,静态类型安全 |
| 架构模式 | 三层架构(Model-Service-Page) | 关注点分离,团队协作高效 |
| 数据流 | @State响应式驱动 | 声明式UI的标准数据流模式 |
| 路由方案 | router API | HarmonyOS原生路由能力 |
| Mock策略 | Service层内置Mock | 便于独立开发和测试 |
| 数据容器 | Record<string, Object> | 灵活的键值对存储,适配不同输入场景 |
关于 ArkTS 语言约束的决策:
ArkTS 是 TypeScript 的子集,但做了大量严格的语法限制。经过分析,我们确认了以下关键约束并制定了对应的编码规范:
- 不支持
any和unknown类型 → 所有变量必须显式指定类型 - 不支持解构赋值 → 使用临时变量逐字段操作
- 不支持
Function.bind/Function.apply→ 遵循传统 OOP 风格处理this - 不支持索引签名 → 使用数组替代
- 不支持
for...in遍历对象 → 使用常规 for 循环迭代数组 - 不支持对象字面量直接作为类型 → 显式声明类和接口
- 不支持
in运算符 → 使用instanceof替代 - 不支持函数表达式 → 使用箭头函数
1.5 需求规格确认
经过多轮沟通和确认,我们最终形成了以下精确的需求规格:
用户输入字段:
- 目的地(destination):用户期望前往的旅行目的地
- 出行方式(transport):飞机、高铁、自驾、骑行等
- 预算(budget):旅行的总预算金额
AI 输出字段:
- 行程安排(itinerary):每日行程列表
- 天数(day):旅行总天数
- 景点列表(spots):推荐景点
- 名称(name):规划名称
- 类型(type):旅行类型
- 时长(duration):旅行时长
- 费用(cost):预估总费用
- 提示(tip):旅行小贴士
- 三餐推荐(breakfast/lunch/dinner):每日餐饮推荐
- 交通详情(transport_detail):详细交通方案
- 交通方式(transport):出行方式
- 住宿推荐(accommodation):住宿建议
- 美食推荐(food):特色美食
- 门票信息(tickets):景点门票价格
- 其他费用(other):其他杂项费用
- 预算总额(budget):总预算
- 当地美食(local_food):当地特色美食列表
- 推荐店铺(where):美食推荐店铺
- 人均价格(price):人均消费
- 避坑指南(pitfalls):旅行注意事项列表
- 应急信息(emergency):当地应急联系方式
2. 架构阶段(Architect)
架构阶段的目标是从共识文档出发,设计出系统化的技术架构方案。在 “AI旅行规划师” 项目中,我们基于对齐阶段确认的需求规格,进行了详细的架构设计。
2.1 整体架构设计
“AI旅行规划师” 采用经典的 MVVM 变体架构,结合 HarmonyOS ArkTS 的特性,形成了 “Model-Service-Page” 三层架构模式:
┌─────────────────────────────────────────────────────────────┐
│ Page层 (View) │
│ AI旅行规划师Page.ets │
│ ├── @State 状态变量定义 │
│ ├── build() 声明式UI构建 │
│ ├── 用户输入表单(目的地、出行方式、预算) │
│ ├── 生成按钮(调用Service生成规划) │
│ └── 结果展示区域(行程、景点、餐饮、交通等) │
├─────────────────────────────────────────────────────────────┤
│ Service层 (ViewModel) │
│ AI旅行规划师Service.ets │
│ ├── generateData() 数据生成逻辑 │
│ ├── AI Prompt 构建 │
│ ├── 输入参数解析与验证 │
│ └── 数据转换与格式化 │
├─────────────────────────────────────────────────────────────┤
│ Model层 (Model) │
│ AI旅行规划师Model.ets │
│ ├── AI旅行规划师Data 数据类定义 │
│ ├── 25+ 个业务字段定义 │
│ └── 构造函数初始化 │
├─────────────────────────────────────────────────────────────┤
│ HarmonyOS 平台层 │
│ ├── ArkUI 框架 (声明式UI) │
│ ├── router 路由系统 │
│ └── 系统能力API │
└─────────────────────────────────────────────────────────────┘
2.2 核心数据流设计
数据流是应用的核心脉络。在 “AI旅行规划师” 中,数据流遵循以下路径:
用户输入 → @State 绑定 → 点击生成 → Service.generateData()
→ Model 实例化 → 数据填充 → 返回结果 → @State 更新
→ UI 自动重渲染 → 结果展示
具体来说,整个数据流可以分为以下几个阶段:
1. 输入阶段: 用户通过三个 TextInput 组件分别输入目的地、出行方式和预算。每个 TextInput 组件都通过 onChange 回调监听输入内容的变化,并将数据同步到 @State inputData 字典中。TextInput 组件是 ArkUI 中最常用的输入组件之一,它支持占位符、输入类型限制、最大长度等多种配置。
2. 状态绑定: 输入内容通过 onChange 回调同步到 @State inputData 字典。Record<string, Object> 类型的 inputData 充当了一个灵活的数据容器,可以容纳不同数量和类型的输入字段。这种设计使得后续添加新的输入字段时,不需要修改 Page 层的数据结构,只需在 UI 中添加对应的 TextInput 组件即可。
3. 触发生成: 点击"生成海报"按钮,调用 Service.generateData() 方法。按钮的 onClick 回调是触发 AI 生成的核心事件。在回调中,我们同时做了两件事:调用 Service 生成数据,以及设置 showResult = true 展示结果。这种设计将"数据处理"和"UI展示"两个关注点清晰地分离开来。
4. 数据处理: Service 层根据输入参数,生成结构化的旅行规划数据。当前使用的是 Mock 数据,后续可以无缝替换为真实 AI 大模型调用。Service 层的 generateData 方法接收一个 Record<string, Object> 参数,返回一个 AI旅行规划师Data 对象——这是整个应用的核心数据流通道。
5. 结果更新: 返回的 AI旅行规划师Data 对象赋值给 @State resultData。这一步触发 ArkUI 的响应式更新机制,框架会自动检测到 resultData 的变化,并重新渲染依赖于它的 UI 组件。
6. UI 渲染: ArkUI 检测到 @State 变量变化,自动触发 UI 重渲染。在声明式 UI 框架中,开发者只需要描述"UI 应该是什么样子",而不需要关心"UI 如何更新"。框架内部通过虚拟 DOM diff 算法,计算出最小的 UI 更新范围,从而实现高效的渲染。
2.3 模块依赖关系
在 “AI旅行规划师” 中,模块之间的依赖关系如下:
AI旅行规划师Page.ets
├── import { AI旅行规划师Data } from './AI旅行规划师Model'
├── import { AI旅行规划师Service } from './AI旅行规划师Service'
└── import { router } from '@kit.ArkUI'
AI旅行规划师Service.ets
└── import { AI旅行规划师Data } from './AI旅行规划师Model'
AI旅行规划师Model.ets
└── (无外部依赖)
这种单向依赖关系确保了架构的清晰性:Page 依赖 Service 和 Model,Service 依赖 Model,Model 不依赖任何模块。这是典型的"高层依赖低层"的原则体现。
2.4 数据模型设计
数据模型是应用的数据骨架。AI旅行规划师Data 类定义了完整的旅行规划数据结构:
// 文件路径:entry/src/main/ets/apps/AI旅行规划师/AI旅行规划师Model.ets
export class AI旅行规划师Data {
itinerary: string[] = []
day: string = ''
spots: string[] = []
name: string = ''
type: string = ''
duration: string = ''
cost: string = ''
tip: string = ''
breakfast: string = ''
lunch: string = ''
dinner: string = ''
transport_detail: string = ''
transport: string = ''
accommodation: string = ''
food: string = ''
tickets: string = ''
other: string = ''
budget: string = ''
local_food: string[] = []
where: string = ''
price: string = ''
pitfalls: string[] = []
emergency: string = ''
constructor() {
this.itinerary = []
this.day = ''
this.spots = []
this.name = ''
this.type = ''
this.duration = ''
this.cost = ''
this.tip = ''
this.breakfast = ''
this.lunch = ''
this.dinner = ''
this.transport_detail = ''
this.transport = ''
this.accommodation = ''
this.food = ''
this.tickets = ''
this.other = ''
this.budget = ''
this.local_food = []
this.where = ''
this.price = ''
this.pitfalls = []
this.emergency = ''
}
}
设计要点分析:
1. 字段全面覆盖: 数据模型包含 25 个字段,覆盖了旅行规划的各个维度——行程(itinerary、day、spots)、基本信息(name、type、duration、cost、tip)、餐饮(breakfast、lunch、dinner)、交通(transport_detail、transport)、住宿(accommodation)、美食(food、local_food、where、price)、门票(tickets)、预算(budget)、避坑(pitfalls)、应急(emergency)等。这种全面的字段设计确保了旅行规划信息的完整性和系统性。
2. 数组与字符串的混合使用: 模型中既有 string[] 类型的数组字段(如 itinerary、spots、local_food、pitfalls),也有 string 类型的标量字段(如 day、name、type 等)。数组字段用于存储列表型数据(如多个景点、多个避坑提示),标量字段用于存储单值数据(如总天数、总预算)。这种设计在 ArkTS 中是完全合法的,因为 ArkTS 不支持索引签名,但支持标准的数组类型。
3. 字段初始化与构造函数的双重保障: 所有字段在声明时都进行了空值初始化,在构造函数中又重复初始化了一次。这种双重保险的设计是为了确保在极端情况下(如序列化/反序列化)数据的完整性。在 ArkTS 中,class 的字段声明和构造函数中的赋值各有其用途:字段声明提供了编译时的类型检查,构造函数赋值提供了运行时的初始化保证。
4. 与 ArkTS 语法约束的兼容性: 模型类中所有字段都有明确的类型标注(string 或 string[]),没有使用 any 或 unknown 类型;没有使用索引签名;没有使用解构赋值;没有使用对象字面量作为类型。这些做法完全符合 ArkTS 的语法约束。
2.5 Service 层设计
Service 层是业务逻辑的核心,负责数据生成和 AI Prompt 的调用:
// 文件路径:entry/src/main/ets/apps/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()
// Mock data generation based on input
let destinationVal: string = String(input['destination'] || '')
result.itinerary = ['示例数据1', '示例数据2', '示例数据3']
result.transport_detail = '生成结果:' + destinationVal
result.budget = '生成结果:' + destinationVal
result.local_food = ['示例数据1', '示例数据2', '示例数据3']
result.pitfalls = ['示例项1', '示例项2', '示例项3']
result.emergency = '生成结果:' + destinationVal
return result
}
}
设计要点分析:
1. 输入参数类型: input: Record<string, Object> 使用了 Record 工具类型——这是 ArkTS 支持的少数几个 TypeScript 实用类型之一。Record<K, V> 用于表示键值对映射,索引表达式 rec[index] 的类型为 V | undefined。这里需要注意,访问 input['destination'] 时可能返回 undefined,因此代码中使用了 || 运算符提供默认值。
2. 类型转换: String(input['destination'] || '') 进行了显式类型转换。在 ArkTS 中,不支持隐式类型转换,如 + 运算符作用于字符串等操作必须显式进行。这是 ArkTS 与 TypeScript 的重要区别之一。
3. Mock 数据策略: 当前 Service 层使用 Mock 数据作为占位,generateData 方法根据输入参数构建包含占位符的示例数据。这种策略允许 UI 层在 AI 服务就绪之前独立开发和测试。后续接入真实 AI 大模型时,只需替换 generateData 方法的内部实现,对外接口保持不变。
4. 返回新实例: 每次调用 generateData 都创建一个新的 AI旅行规划师Data 实例,而不是复用内部 model 成员。这是为了避免状态污染,确保每次生成都是独立的、无副作用的调用。
5. 数组字段的 Mock: 对于 itinerary、local_food、pitfalls 等数组字段,Mock 数据使用固定长度的字符串数组。这种设计确保了 UI 层的 ForEach 循环渲染可以正常工作,验证列表布局的完整性和正确性。
2.6 Page 层设计
Page 层是应用的 UI 界面,使用 ArkUI 声明式语法构建。整个页面可以分为三个主要区域:顶部导航栏、输入表单区域、结果展示区域。
顶部导航栏:
顶部导航栏使用 Row 和 Column 的组合布局,左侧显示返回按钮,中间显示应用名称和副标题,右侧显示装饰图标:
Row() {
Text('← 返回')
.fontSize(13)
.fontColor('#8B4513')
.onClick(() => { router.back() })
Blank()
Column() {
Text('📱 AI旅行规划师')
.fontSize(17)
.fontWeight(FontWeight.Bold)
.fontColor('#2C1810')
Text('POSTER · 旅行海报')
.fontSize(9)
.fontColor('#8B4513')
.letterSpacing(2)
.margin({ top: 2 })
}
Blank()
Text('🎨').fontSize(22)
}
.width('100%')
.padding({ left: 20, right: 20, top: 16, bottom: 14 })
.backgroundColor('#F5E6D3')
输入表单区域:
输入表单区域包含三个输入项——目的地、出行方式、预算,每个输入项配有一个标签和 TextInput 组件:
Column() {
Text('目的地')
.fontSize(11).fontColor('#8B4513').margin({ top: 6, bottom: 3 })
TextInput({ placeholder: '请输入目的地' })
.fontSize(13).height(40).backgroundColor('#FFF8F0')
.borderRadius(0).border({ width: 1, color: '#D4A574' })
.padding({ left: 12, right: 12 })
.onChange((val: string) => { this.inputData['目的地'] = val })
Text('出行方式')
.fontSize(11).fontColor('#8B4513').margin({ top: 6, bottom: 3 })
TextInput({ placeholder: '请输入出行方式' })
.fontSize(13).height(40).backgroundColor('#FFF8F0')
.borderRadius(0).border({ width: 1, color: '#D4A574' })
.padding({ left: 12, right: 12 })
.onChange((val: string) => { this.inputData['出行方式'] = val })
Text('预算')
.fontSize(11).fontColor('#8B4513').margin({ top: 6, bottom: 3 })
TextInput({ placeholder: '请输入预算' })
.fontSize(13).height(40).backgroundColor('#FFF8F0')
.borderRadius(0).border({ width: 1, color: '#D4A574' })
.padding({ left: 12, right: 12 })
.onChange((val: string) => { this.inputData['预算'] = val })
}
.width('100%').padding(18).backgroundColor('#FFF8F0')
.border({ width: 2, color: '#D4A574' }).margin({ top: 6 })
生成按钮:
生成按钮触发 AI 旅行规划生成,使用暖色调的棕色背景和金色文字,突出可点击性:
Button('📱 🎨 生成海报')
.width('100%').height(50).backgroundColor('#8B4513')
.borderRadius(0).fontColor('#FFD700').fontSize(16)
.fontWeight(FontWeight.Bold).margin({ top: 18, bottom: 14 })
.onClick(() => {
this.resultData = this.service.generateData(this.inputData)
this.showResult = true
})
结果展示区域:
结果展示区域使用条件渲染,仅在生成结果后显示。区域内包含多个数据展示块,涵盖行程、景点、餐饮、交通、住宿、预算、美食、避坑、应急等各类信息:
if (this.showResult && this.resultData !== null) {
Column() {
Text('🎨 海报成品').fontSize(15).fontWeight(FontWeight.Bold)
.fontColor('#2C1810').margin({ bottom: 12 })
// 行程安排列表
Text('Itinerary').fontSize(13).fontWeight(FontWeight.Bold)
.fontColor('#333333').margin({ top: 10, bottom: 6 })
if (this.resultData.itinerary) {
ForEach(this.resultData.itinerary, (item: string, index: number) => {
Row() {
Text('• ').fontSize(12).fontColor('#666666')
Text(item).fontSize(12).fontColor('#333333')
}.width('100%').padding({ top: 2, bottom: 2 })
}, (item: string, index: number) => index.toString())
}
// 天数
Row() {
Text('Day: ').fontSize(12).fontWeight(FontWeight.Medium).fontColor('#666666')
Text(this.resultData.day).fontSize(12).fontColor('#333333')
}.width('100%').padding({ top: 4, bottom: 4 })
// 景点列表
Text('Spots').fontSize(13).fontWeight(FontWeight.Bold)
.fontColor('#333333').margin({ top: 10, bottom: 6 })
if (this.resultData.spots) {
ForEach(this.resultData.spots, (item: string, index: number) => {
Row() {
Text('• ').fontSize(12).fontColor('#666666')
Text(item).fontSize(12).fontColor('#333333')
}.width('100%').padding({ top: 2, bottom: 2 })
}, (item: string, index: number) => index.toString())
}
// ... 其他字段的展示(name, type, duration, cost, tip 等)
}
.width('100%').padding(18).backgroundColor('#FFF8F0')
.border({ width: 2, color: '#D4A574' }).margin({ bottom: 20 })
}
Page 层完整代码:
// 文件路径:entry/src/main/ets/apps/AI旅行规划师/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('#8B4513')
.onClick(() => { router.back() })
Blank()
Column() {
Text('📱 AI旅行规划师').fontSize(17).fontWeight(FontWeight.Bold).fontColor('#2C1810')
Text('POSTER · 旅行海报').fontSize(9).fontColor('#8B4513').letterSpacing(2).margin({ top: 2 })
}
Blank()
Text('🎨').fontSize(22)
}
.width('100%').padding({ left: 20, right: 20, top: 16, bottom: 14 })
.backgroundColor('#F5E6D3')
// 内容区域
Scroll() {
Column() {
// 输入表单
Column() {
Text('目的地').fontSize(11).fontColor('#8B4513').margin({ top: 6, bottom: 3 })
TextInput({ placeholder: '请输入目的地' })
.fontSize(13).height(40).backgroundColor('#FFF8F0').borderRadius(0)
.border({ width: 1, color: '#D4A574' }).padding({ left: 12, right: 12 })
.onChange((val: string) => { this.inputData['目的地'] = val })
Text('出行方式').fontSize(11).fontColor('#8B4513').margin({ top: 6, bottom: 3 })
TextInput({ placeholder: '请输入出行方式' })
.fontSize(13).height(40).backgroundColor('#FFF8F0').borderRadius(0)
.border({ width: 1, color: '#D4A574' }).padding({ left: 12, right: 12 })
.onChange((val: string) => { this.inputData['出行方式'] = val })
Text('预算').fontSize(11).fontColor('#8B4513').margin({ top: 6, bottom: 3 })
TextInput({ placeholder: '请输入预算' })
.fontSize(13).height(40).backgroundColor('#FFF8F0').borderRadius(0)
.border({ width: 1, color: '#D4A574' }).padding({ left: 12, right: 12 })
.onChange((val: string) => { this.inputData['预算'] = val })
}
.width('100%').padding(18).backgroundColor('#FFF8F0')
.border({ width: 2, color: '#D4A574' }).margin({ top: 6 })
// 生成按钮
Button('📱 🎨 生成海报').width('100%').height(50)
.backgroundColor('#8B4513').borderRadius(0).fontColor('#FFD700')
.fontSize(16).fontWeight(FontWeight.Bold).margin({ top: 18, bottom: 14 })
.onClick(() => {
this.resultData = this.service.generateData(this.inputData)
this.showResult = true
})
// 结果展示区域
if (this.showResult && this.resultData !== null) {
// ... 结果展示内容
}
}
.width('100%').padding({ left: 18, right: 18, bottom: 40 })
}
.layoutWeight(1)
}
.width('100%').height('100%').backgroundColor('#F5E6D3')
}
}
3. 原子化阶段(Atomize)
更多推荐



所有评论(0)