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 的子集,但做了大量严格的语法限制。经过分析,我们确认了以下关键约束并制定了对应的编码规范:

  • 不支持 anyunknown 类型 → 所有变量必须显式指定类型
  • 不支持解构赋值 → 使用临时变量逐字段操作
  • 不支持 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 语法约束的兼容性: 模型类中所有字段都有明确的类型标注(stringstring[]),没有使用 anyunknown 类型;没有使用索引签名;没有使用解构赋值;没有使用对象字面量作为类型。这些做法完全符合 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: 对于 itinerarylocal_foodpitfalls 等数组字段,Mock 数据使用固定长度的字符串数组。这种设计确保了 UI 层的 ForEach 循环渲染可以正常工作,验证列表布局的完整性和正确性。

2.6 Page 层设计

Page 层是应用的 UI 界面,使用 ArkUI 声明式语法构建。整个页面可以分为三个主要区域:顶部导航栏、输入表单区域、结果展示区域。

顶部导航栏:

顶部导航栏使用 RowColumn 的组合布局,左侧显示返回按钮,中间显示应用名称和副标题,右侧显示装饰图标:

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)

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐