摘要

「衣品进化室」是 AI40 智能应用工具箱中编号为 1 的品质生活类应用,核心功能是根据用户选择的气温区间、穿着场合和风格偏好,智能生成一套完整的穿搭方案,包括上装、下装、鞋履、配饰搭配、推荐理由、备选方案以及穿搭小贴士。本文从 6A 工作流(Align-Architect-Atomize-Approve-Automate-Assess)的视角,系统性地记录了该应用从 Prompt 模板到 ArkTS 代码实现的完整过程,深入分析了 ArkTS 严格模式下的类型定义、状态管理、UI 组件设计和 Mock 数据策略,为 HarmonyOS 开发者提供了一套可复用的 AI 应用开发范式。


一、对齐阶段(Align):从 Prompt 到需求规范

1.1 应用背景与 Prompt 分析

在这里插入图片描述
在这里插入图片描述

系统指令: 将 AI 角色定义为一个"每日穿搭方案"数据生成器,要求只返回符合 JSON Schema 的数据,禁止输出任何解释、问候或免责声明。搭配需符合所选气温与场合,reason 字段必须说明搭配逻辑且可核验。temperature 参数设置为 0.4,在准确性和创意性之间取得平衡。

用户输入 Schema: 三个维度的参数选择——气温(temperature,枚举值 15-20℃、20-25℃、25-30℃、30-35℃)、场合(occasion,枚举值通勤、约会、运动、居家、正式)、风格(style,枚举值简约、休闲、甜美、商务、街头)。

输出格式: 一个 JSON 对象,包含 top(上装描述)、bottom(下装描述)、shoes(鞋履描述)、accessory(配饰描述)、reason(搭配理由,需结合气温、场合、风格三个维度)、alt(备选搭配)六个字段。

兜底规则: 当用户输入信息不足时,默认按"通勤 + 简约 + 20℃"生成方案。

这个 Prompt 的设计体现了几个关键特点:首先,三个维度的参数设计覆盖了影响穿搭决策的核心因素——气温决定了衣物的厚薄和材质,场合决定了正式程度和风格走向,个人风格偏好则提供了个性化空间。其次,输出格式要求搭配理由必须"可核验",这意味着 AI 不能凭空编造,而是需要建立气温、场合、风格与具体衣物之间的逻辑关联。最后,temperature=0.4 的设置表明这是一个需要一定创意但不希望过于随机的场景——穿搭建议需要既合理又有个性。

1.2 需求理解与功能边界确认

基于 Prompt 分析,我们确定了「衣品进化室」的功能边界:

核心功能: 用户选择气温、场合、风格三个参数后,点击生成按钮,获得一套完整的穿搭方案。方案包括五个部分:上装、下装、鞋履、配饰(含推荐理由)、备选搭配和穿搭小贴士。

不在范围内: 不提供衣物购买链接(非电商场景)、不支持用户自定义体型数据(如身高、体重、肤色,这些是后续扩展方向)、不支持用户上传已有衣物进行智能搭配(需要图像识别能力)。

交互流程: 参数选择(三个下拉选择器)→ 点击"AI 生成"按钮 → 显示 Loading 动画(800ms 模拟延迟)→ 展示结果。这是一个典型的"输入 → 处理 → 输出"三段式交互,用户操作路径清晰,无需引导。

离线策略: 内置 4 组 Mock 数据场景,分别覆盖三个精确匹配场景和一个通用兜底场景。精确匹配场景为:20-25℃通勤简约、15-20℃约会甜美、30-35℃运动休闲。这三个场景覆盖了最典型的穿搭需求组合。兜底场景使用基础款白 T + 牛仔裤的百搭方案,确保任何参数组合都能获得可用结果。

1.3 技术约束确认

在 ArkTS 严格模式下,我们需要特别关注以下技术约束对本应用的影响:

  1. 禁止 any 类型: 穿搭结果的数据结构必须通过显式 interface 定义,不能使用 any 或 unknown 类型。这意味着我们需要定义 OutfitItem(单品信息)和 OutfitOutput(完整输出)两个接口。

  2. 禁止解构赋值: 在 generateMockData() 方法中,不能使用 const { temperature, occasion, style } = this 这样的解构语法,必须逐字段赋值。

  3. 禁止 for…in 循环: 选项列表的遍历全部使用 ForEach 组件或常规 for 循环,不能使用 for…in。

  4. @State 属性名冲突风险: 虽然本应用中没有直接使用 height、weight 等与 ArkUI 内置属性同名的变量,但选择了 temperature 作为参数名,这是一个安全的命名选择。

  5. Scroll 组件单子组件约束: 所有可滚动内容必须包裹在一个 Column 中,作为 Scroll 的唯一直接子组件。

1.4 验收标准

维度 标准 验证方式
参数覆盖 气温 4 种 × 场合 5 种 × 风格 5 种 = 100 种组合 至少 3 组精确匹配 + 1 组兜底
结果完整性 每次生成包含上装、下装、鞋履、配饰、理由、备选、贴士 7 个部分 结果展示验证
离线可用 无网络环境下正常生成结果 飞行模式测试
编译合规 零 Error,通过 ArkTS 严格模式 DevEco Studio 编译
UI 交互 三个下拉选择器可正常展开/收起/选中 手动操作验证

二、架构阶段(Architect):数据结构与组件设计

2.1 数据接口设计

「衣品进化室」的数据模型相对简洁,核心是两个接口:

// 单品信息:描述穿搭方案中的每个组成部分
interface OutfitItem {
  top: string;          // 上装描述,如 "米白色亚麻衬衫"
  bottom: string;       // 下装描述,如 "卡其色直筒西裤"
  shoes: string;        // 鞋履描述,如 "白色乐福鞋"
  accessory: string;    // 配饰描述,如 "玫瑰金细项链 + 棕色托特包"
  reason: string;       // 搭配理由,需结合气温+场合+风格,可核验
  alt: string;          // 备选搭配方案
}

// 完整输出:穿搭方案 + 穿搭小贴士
interface OutfitOutput {
  recommendation: OutfitItem;
  tips: string;         // 穿搭小贴士,如 "推荐选择浅色系搭配..."
}

这个接口设计有几个值得注意的细节:

嵌套结构的选择: 我们将穿搭单品(OutfitItem)和贴士(tips)放在同一个 OutfitOutput 接口中,而不是分开。这样做的原因是:穿搭方案和贴士是强关联的——贴士的内容通常与具体的穿搭方案相关(如"推荐选择浅色系搭配"是针对某套具体搭配的建议),将它们放在同一个数据结构中,便于在 UI 中一次性展示,也便于在 Mock 数据中统一管理。

字段粒度的权衡: 我们没有将上装拆分为更细粒度的字段(如"上衣类型"、“上衣颜色”、“上衣材质”),而是使用一个描述性字符串。这是因为在 Prompt 模板中,top 字段就是一个自由文本,AI 生成的描述格式不固定。如果拆分为更细粒度的字段,需要 AI 严格按照格式输出,增加了 Prompt 的复杂度和出错概率。在 UI 展示层面,我们只需要将描述文本直接显示即可,不需要对每个字段单独渲染。

备选方案的设计: alt 字段被设计为单个字符串而非数组。在原 Prompt 模板中,alt 要求返回"备选搭配1"和"备选搭配2"两个方案。但在实际 UI 中,我们将其合并为一个字符串展示,因为两个备选方案在视觉上可以放在同一行,用一个蓝色的背景区块统一展示即可,不需要额外的列表渲染逻辑。

2.2 状态管理设计

本应用使用 6 个 @State 变量管理页面状态:

@State selectedTemperature: string = '20-25℃';   // 选中的气温
@State selectedOccasion: string = '通勤';          // 选中的场合
@State selectedStyle: string = '简约';             // 选中的风格
@State outputData: OutfitOutput | null = null;     // 生成结果
@State isLoading: boolean = false;                 // 加载状态
@State hasResult: boolean = false;                 // 是否有结果

状态变量的设计遵循了以下原则:

分离用户输入和生成结果: 前三个变量(selectedTemperature、selectedOccasion、selectedStyle)存储用户的选择参数,后三个变量(outputData、isLoading、hasResult)管理生成结果和 UI 状态。这种分离使得状态变更的触发条件清晰——用户选择参数时只更新前三个变量,点击生成按钮时更新后三个变量。

使用联合类型表达空值: outputData 的类型为 OutfitOutput | null,初始值为 null。这与 ArkTS 不支持 optional chaining(可选链)的特性有关——我们不能使用 this.outputData?.recommendation 这样的语法。在 ArkTS 中,我们需要在访问 outputData 的属性之前,先通过 if (this.outputData !== null) 进行空值检查。

使用 hasResult 控制 UI 显示: 虽然我们可以通过 this.outputData !== null 来判断是否有结果,但单独使用一个 hasResult 布尔变量有额外的好处:在切换参数重新生成时,我们可以先将 hasResult 设为 false 来隐藏旧结果,等新数据准备好后再设为 true 显示新结果。这避免了在数据更新过程中出现"闪烁"(旧结果短暂显示后替换为新结果)。

静态数据使用 private 而非 @State: 选项列表(temperatureOptions、occasionOptions、styleOptions)使用 private 修饰,不标记为 @State。因为这些数据在应用生命周期内不会改变,不需要触发 UI 重渲染。将它们标记为 @State 会导致不必要的性能开销——每次这些"常量"数组被重新赋值时,都会触发整个组件的重渲染。private 关键字确保了它们只能被组件内部方法访问,但不会触发响应式更新。

2.3 UI 组件树设计

App1 (@Entry, @Component)
├── Column (根容器)
│   ├── Row (TopBar 返回栏)
│   │   ├── Text("← 返回") → onClick → router.back()
│   │   ├── Text("衣品进化室") → 居中标题
│   │   └── Text("") → 占位元素,保持标题居中
│   └── Scroll (可滚动内容区)
│       └── Column (唯一子组件)
│           ├── 气温选择器 (下拉展开/收起)
│           │   ├── Text("气温选择") → 标签
│           │   ├── Row → 当前选中值 + "▼" 箭头
│           │   └── if (showTempPicker) → Column
│           │       └── ForEach(temperatureOptions)
│           │           └── Row → 选项文字 + ✓ 选中标记
│           ├── 场合选择器 (同气温选择器结构)
│           ├── 风格选择器 (同气温选择器结构)
│           ├── Row → Button("AI 生成") → onClick → onGenerate()
│           ├── if (isLoading) → LoadingProgress + 提示文字
│           └── if (hasResult && outputData !== null) → 结果展示区
│               ├── Text("穿搭推荐") → 区块标题
│               ├── Column → 穿搭详情卡片
│               │   ├── Row → "上衣" + top 描述
│               │   ├── Row → "下装" + bottom 描述
│               │   ├── Row → "鞋子" + shoes 描述
│               │   └── Row → "配饰" + accessory 描述
│               ├── Text("推荐理由") + reason 区块
│               ├── Text("备选搭配") + alt 区块
│               └── Text("穿搭小贴士") + tips 区块

2.4 下拉选择器组件的设计模式

「衣品进化室」中三个参数选择器都采用了下拉展开/收起的交互模式。这个模式在 ArkUI 中的实现值得单独分析:

// 选择器当前状态展示
Row() {
  Text(this.selectedTemperature)    // 当前选中值
    .fontSize(14)
    .fontColor('#333333')
    .layoutWeight(1)
  Text('▼')                         // 下拉箭头
    .fontSize(12)
    .fontColor('#8E8E93')
}
.width('100%')
.padding({ left: 12, right: 12, top: 10, bottom: 10 })
.backgroundColor('#FFFFFF')
.borderRadius(8)
.border({ width: 1, color: '#D1D1D6' })
.onClick((): void => {
  this.showTempPicker = !this.showTempPicker;  // 切换展开/收起
})

// 选项列表(条件渲染)
if (this.showTempPicker) {
  Column() {
    ForEach(this.temperatureOptions, (item: string): void => {
      Row() {
        Text(item)
          .fontColor(item === this.selectedTemperature ? '#007AFF' : '#333333')
          .layoutWeight(1)
        if (item === this.selectedTemperature) {
          Text('✓').fontColor('#007AFF')   // 当前选中项标记
        }
      }
      .backgroundColor(item === this.selectedTemperature ? '#F0F8FF' : '#FFFFFF')
      .onClick((): void => {
        this.selectedTemperature = item;     // 选中后更新状态
        this.showTempPicker = false;         // 自动收起
      })
    })
  }
}

这个设计模式有几个关键点:

展开/收起状态管理: 使用 showTempPicker(布尔值)控制选项列表的显示/隐藏。初始值为 false,点击当前值区域时翻转。这种简单的 toggle 逻辑避免了复杂的弹出层管理。

选中状态视觉反馈: 当前选中的选项通过蓝色文字(#007AFF)和浅蓝色背景(#F0F8FF)高亮,同时在右侧显示 ✓ 标记。三个反馈信号(颜色、背景、图标)确保了用户在任何光照条件下都能清楚识别当前选中项。

选中后自动收起: 用户点击选项后,除了更新 selectedTemperature 状态外,还自动将 showTempPicker 设为 false,收起选项列表。这减少了一次点击操作,提升了交互效率。

ArkTS 约束下的实现: 注意 ForEach 回调中使用了 if (item === this.selectedTemperature) 条件渲染 ✓ 标记。在 ArkTS 中,if 语句在 build() 方法和 @Builder 方法中可以作为 UI 条件渲染语法使用,这是与标准 TypeScript 的一个重要区别。


三、原子化阶段(Atomize):Mock 数据策略

3.1 场景匹配逻辑

「衣品进化室」的 Mock 数据生成采用了基于 if-else 条件链的精确匹配策略,而非其他应用中使用的场景键匹配策略。这两种策略的选择取决于参数维度数:

为什么选择 if-else 而非场景键匹配: 本应用有三个参数维度,每个维度有 4-5 个选项,理论组合数为 100 种。但实际 Mock 数据只需要覆盖 4 个场景(3 个精确匹配 + 1 个兜底)。在这种情况下,if-else 条件链比场景键匹配更直观——每个条件分支直接对应一个实际的穿搭场景,代码可读性更好。

generateMockData(): void {
  const temp: string = this.selectedTemperature;
  const occasion: string = this.selectedOccasion;
  const style: string = this.selectedStyle;

  // 场景1:春秋通勤简约风
  if (temp === '20-25℃' && occasion === '通勤' && style === '简约') {
    this.outputData = { ... };
  }
  // 场景2:微凉约会甜美风
  else if (temp === '15-20℃' && occasion === '约会' && style === '甜美') {
    this.outputData = { ... };
  }
  // 场景3:高温运动休闲风
  else if (temp === '30-35℃' && occasion === '运动' && style === '休闲') {
    this.outputData = { ... };
  }
  // 场景4:兜底方案
  else {
    this.outputData = { ... };
  }
}

3.2 Mock 数据内容分析

让我们深入分析第一个场景的 Mock 数据,理解其设计思路:

场景一:20-25℃ 通勤 简约

{
  recommendation: {
    top: '米白色亚麻衬衫',
    bottom: '卡其色直筒西裤',
    shoes: '白色乐福鞋',
    accessory: '玫瑰金细项链 + 棕色托特包',
    reason: '20-25℃是通勤黄金温度,亚麻衬衫透气不闷热,卡其色直筒裤修饰腿型,乐福鞋兼顾舒适与正式感,整套搭配简约大方适合职场。',
    alt: '备选:雾霾蓝针织衫 + 白色九分裤 + 黑色尖头平底鞋'
  },
  tips: '推荐选择浅色系搭配,视觉上更清爽干练。通勤场合建议避免过于花哨的配饰,保持专业感。'
}

气温匹配: 20-25℃ 是春秋季的典型温度。亚麻材质具有优秀的透气性,适合这个温度区间的通勤穿着。既不会像夏季面料那样过于单薄,也不会像冬季面料那样闷热。

场合匹配: 通勤场合要求"正式但不死板"。米白色亚麻衬衫和卡其色直筒西裤的组合处于"商务休闲"(Business Casual)的范畴,既保持了职场的专业感,又不会像全套西装那样过于正式。乐福鞋是通勤鞋履的经典选择——比运动鞋正式,比高跟鞋舒适。

风格匹配: 简约风格体现在色彩搭配上——米白色 + 卡其色 + 白色,形成了统一的浅色系渐变,没有花哨的图案或装饰。配饰选择了玫瑰金细项链和棕色托特包,低调但有质感,符合简约风格"少即是多"的理念。

理由可核验性: reason 字段中的每个论点都可以独立验证——"亚麻透气"是材质特性,"直筒裤修饰腿型"是版型功能,"乐福鞋兼顾舒适与正式"是鞋履类型特征。这种可核验性是大模型生成内容质量的重要保障。

3.3 参数传递与类型安全

在 ArkTS 严格模式下,generateMockData() 方法中参数的使用方式值得特别关注。由于不支持解构赋值,我们不能使用 const { selectedTemperature: temp, selectedOccasion: occasion, selectedStyle: style } = this 这样的语法,而必须逐字段赋值:

generateMockData(): void {
  const temp: string = this.selectedTemperature;
  const occasion: string = this.selectedOccasion;
  const style: string = this.selectedStyle;
  // 后续逻辑...
}

这种写法的好处是类型标注清晰,每个局部变量的类型都显式可见。但代价是代码行数增加,每个从 this 上取值的属性都需要单独一行。在 ArkTS 中,这是一种必要的"冗余",因为编译器的类型检查需要明确的类型信息来完成静态分析。

另一个值得注意的点是,if-else 条件链中的每个分支都使用了完整的条件表达式(如 temp === '20-25℃' && occasion === '通勤' && style === '简约'),而不是将条件拆分为嵌套的 if 语句。这种扁平化的条件判断在 ArkTS 中更容易被编译器优化,也更容易进行代码覆盖率的分析和测试。

3.4 异步加载模拟

onGenerate() 方法使用 setTimeout 模拟 800ms 的异步延迟,这是为了在离线 Mock 模式下也能给用户提供"AI 正在思考"的感知:

onGenerate(): void {
  this.isLoading = true;
  this.hasResult = false;
  setTimeout((): void => {
    this.generateMockData();
    this.isLoading = false;
    this.hasResult = true;
  }, 800);
}

这 800ms 的延迟并非随意选择。心理学研究表明,用户对即时响应(< 200ms)会感到"太快了,不像 AI",对过长等待(> 3s)会感到焦虑。800ms 接近真实大模型 API 调用的"感知延迟"(实际 API 调用可能需要 2-5 秒,但用户在本地 Mock 场景下不需要等待那么久),既让用户感受到"AI 在工作",又不会因为等待时间过长而产生不耐烦。


四、审批阶段(Approve):关键问题与修复

4.1 ArkTS 类型安全检查

在代码审查中,我们对「衣品进化室」进行了严格的 ArkTS 类型安全检查:

检查项 1:无 any 类型。 所有变量和参数都显式标注了类型。ForEach 回调中的 item 参数标注为 string,onClick 回调的返回类型标注为 void。outputData 的类型为 OutfitOutput | null,是一个明确的联合类型。

检查项 2:无解构赋值。 generateMockData() 方法中使用 const temp: string = this.selectedTemperature 逐字段赋值,而非 const { selectedTemperature: temp } = this 解构语法。

检查项 3:ForEach 回调纯 UI 语法。 所有 ForEach 回调中只包含 UI 组件(Text、Row、Column)和属性链式调用,没有 let 声明、for 循环、if 逻辑判断。条件渲染通过 ArkUI 的 if 语法在组件级别实现。

检查项 4:接口定义完整性。 OutfitItem 和 OutfitOutput 接口的所有字段都标注了具体类型(string),没有使用 any 或 object 类型。

4.2 下拉选择器的交互体验优化

在审批阶段,我们发现了一个潜在的交互问题:当一个下拉选择器展开时,如果用户点击另一个选择器,两个选择器会同时展开,导致 UI 混乱。

解决方案: 在 onClick 回调中,除了翻转当前选择器的展开状态外,同时关闭其他选择器:

// 点击气温选择器时
.onClick((): void => {
  this.showTempPicker = !this.showTempPicker;
  this.showOccasionPicker = false;   // 关闭场合选择器
  this.showStylePicker = false;      // 关闭风格选择器
})

这个改进确保了同一时间只有一个下拉选择器处于展开状态,符合移动端 UI 的常见交互规范。

4.3 结果展示的边界处理

在结果展示区域,我们使用了两层条件检查:

if (this.hasResult && this.outputData !== null) {
  // 展示结果
}

两层检查分别处理了不同的边界情况:hasResult 确保用户已经点击了生成按钮(而非首次进入页面),outputData !== null 确保生成数据确实存在(而非空值)。在 ArkTS 中,由于不支持可选链操作符(?.),这种显式的空值检查是访问可能为 null 的对象属性的必要前提。


五、自动化执行阶段(Automate):代码生成与组装

5.1 代码生成流程

「衣品进化室」的代码生成遵循了 8 部分标准模板:

  1. 导入依赖: import { router } from '@kit.ArkUI'——唯一的外部依赖是路由模块
  2. 接口定义: OutfitItem 和 OutfitOutput 两个接口,共 8 个字段
  3. 入口组件: @Entry @Component struct App1
  4. @State 状态变量: 6 个状态变量,覆盖参数选择和结果管理
  5. 静态数据: 3 个选项数组(private),3 个展开状态变量(private)
  6. Mock 数据生成: generateMockData() 方法,4 个 if-else 条件分支
  7. AI API 预留: callAIAPI() 方法(注释形式),对接 fetch 请求
  8. UI 构建: build() 方法,包含返回栏、三个选择器、生成按钮、Loading 动画、结果展示区

5.2 颜色主题设计

本应用采用粉色系(#FF6B6B)作为主色调,与品质生活分类的整体视觉风格保持一致。颜色使用策略:

  • 主按钮色: #FF6B6B(温暖粉色),用于"AI 生成"按钮
  • Loading 动画色: #FF6B6B,与主按钮色保持一致
  • 结果卡片背景: #FFFFFF(白色),高对比度便于阅读
  • 推荐理由背景: #FFF9E6(浅黄色),从视觉上区分"理由"和"数据"
  • 备选搭配背景: #F0F8FF(浅蓝色),蓝色传达"备选"的低优先级信号
  • 选中状态: #007AFF(系统蓝),遵循 iOS/HarmonyOS 的交互规范

5.3 页面路由集成

返回按钮使用 router.back() 返回首页,不传递任何参数:

Text('← 返回')
  .fontSize(16)
  .fontColor('#007AFF')
  .onClick((): void => { router.back(); })

标题居中采用了"左-中-右"三段式布局:左侧为返回按钮(宽度 60),中间为标题(layoutWeight: 1,自动填充剩余空间),右侧为占位 Text(宽度 60)。这种布局方式确保了标题在任何屏幕宽度下都能精确居中,比使用 textAlign(TextAlign.Center) 在 Row 中居中更可靠。


六、评估阶段(Assess):总结与展望

6.1 技术成果

「衣品进化室」的开发完成了一个完整的 AI 穿搭推荐应用,具有以下技术特点:

  • 使用 ArkTS 严格模式,零 any 类型,接口定义完整
  • 三个下拉选择器实现了展开/收起/自动关闭的完整交互
  • 4 组 Mock 数据覆盖了典型穿搭场景(通勤、约会、运动)和兜底方案
  • 预留了 callAIAPI() 接口,支持未来对接真实大模型 API
  • 完整的 UI 状态管理:默认态、加载态、结果态、空状态

6.2 代码质量指标

维度 指标 说明
总行数 470 行 单文件完整实现
接口数量 2 个 OutfitItem、OutfitOutput
@State 变量 6 个 参数 3 个 + 结果 3 个
Mock 场景 4 个 3 精确匹配 + 1 兜底
组件嵌套层级 最深 5 层 Column > Scroll > Column > Row > Text
ForEach 使用 3 处 三个选择器各一个
条件渲染 5 处 3 个选择器展开 + Loading + 结果

6.3 与同类应用的对比分析

「衣品进化室」在 40 个应用中属于"纯选择型"应用的代表——用户通过预定义的选项(而非自由文本输入)来确定参数,AI 基于参数组合生成结果。这类应用的共性特征包括:

参数设计的维度选择: 本应用选择了气温、场合、风格三个维度。这三个维度是穿搭决策中最核心且最不依赖个人体型数据的因素。与「营养方程式」(App33)需要身高、体重等连续值不同,本应用的参数全部是离散的枚举值,这使得 Mock 数据的场景覆盖更加可控——100 种理论组合中,只需覆盖 4 个代表性场景即可。

下拉选择器的适用性: 当选项数量在 4-6 个之间时,下拉选择器是最佳的交互方式。如果选项超过 8 个,建议使用滚动列表或搜索过滤;如果选项只有 2-3 个,分段控制器(Segmented Control)更加高效。本应用的三个维度分别有 4、5、5 个选项,恰好处于下拉选择器的最佳适用区间。

结果展示的卡片化设计: 穿搭结果被拆分为上装、下装、鞋履、配饰四个独立行,每行包含标签和描述两部分。这种"标签-值"的展示方式适合信息密度较高的结果——用户可以快速扫描标签找到关注的信息,而不需要逐行阅读完整文本。

6.4 性能分析与优化建议

渲染性能: 本应用的结构相对简单,组件嵌套层级最深为 5 层,不涉及大量列表渲染或复杂动画,因此性能瓶颈不明显。但在实际运行中,仍有一些值得关注的性能细节:

选择器展开/收起时的重渲染:showTempPicker 状态变化时,整个 Column 组件树会重新渲染。虽然 ForEach 只渲染了 4 个选项,但每次展开/收起都会触发完整的 diff 计算。如果未来选项数量增加到 20 个以上,建议将选择器抽取为独立的 @Component 子组件,利用 ArkUI 的组件级更新机制减少不必要的重渲染范围。

结果区域的懒渲染: 当前实现中,结果区域通过 if (this.hasResult && this.outputData !== null) 进行条件渲染。在 ArkUI 中,条件渲染的组件在条件为 false 时会被完全销毁(而非隐藏),这避免了不可见组件的内存占用和渲染开销。当条件变为 true 时,组件重新创建并挂载到组件树中。

颜色硬编码的优化空间: 当前代码中所有颜色值都直接使用十六进制字符串(如 ‘#FF6B6B’、‘#007AFF’)。在后续迭代中,建议将这些颜色值迁移到 resources 资源文件中,通过 $r('app.color.xxx') 引用。这样做的好处是:支持深色主题自动切换(系统会根据当前主题选择对应的资源值),便于统一管理和修改主题色。

6.5 可扩展方向

扩展方向一:增加用户体型数据。 当前版本只考虑了气温、场合、风格三个维度。未来可以增加身高、体重、体型(梨形/苹果形/沙漏形等)参数,让 AI 推荐更适合用户身材的穿搭方案。这需要修改 OutfitItem 接口,增加 bodyType 等字段,并在 Prompt 模板中增加对应的体型建议逻辑。例如,对于梨形身材的用户,AI 应推荐上浅下深的配色方案和 A 字裙等修饰下半身的版型。

扩展方向二:衣物库智能搭配。 当前版本是"从零生成"穿搭方案。未来可以支持用户上传自己的衣物照片,建立个人衣物库,AI 从已有衣物中进行搭配推荐。这需要引入图像识别能力(识别衣物颜色、类型、风格)和衣物数据库管理(存储衣物元数据和照片)。技术路线可以是:图像识别 → 衣物特征提取 → 衣物数据库 → 基于约束的搭配推荐。

扩展方向三:季节感知与天气联动。 当前版本的气温区间需要用户手动选择。未来可以通过获取设备位置和天气 API,自动推荐适合当天天气的穿搭方案,减少用户的操作步骤。HarmonyOS 提供了 @ohos.geoLocationManager 位置服务和 @ohos.systemWeather 天气服务,可以获取设备的当前位置和实时天气数据。

扩展方向四:社交分享与穿搭日记。 穿搭方案可以支持一键分享到社交平台,生成精美的穿搭卡片图片,包含衣物图示和搭配说明。同时可以建立穿搭日记功能,记录每天的穿搭选择,形成个人风格档案,帮助 AI 更好地理解用户的穿搭偏好。

6.6 开发经验总结

下拉选择器在 ArkTS 中的最佳实践: 使用条件渲染(if)实现展开/收起,比使用 Navigation 或弹出层更简单直接。对于选项数量较少(4-5 个)的选择器,这种方案在性能和用户体验上都是最优解。关键实现要点包括:独立的展开状态变量、选中后自动收起、互斥展开(同时只有一个选择器展开)、清晰的选中状态视觉反馈。

Mock 数据策略的选择标准: 当参数维度少且 Mock 场景少时,if-else 条件链比场景键匹配更直观。当参数维度多或 Mock 场景多时,场景键匹配更易维护。选择策略的标准是"代码可读性优先于通用性"——在满足当前需求的前提下,选择最简单的实现方式,避免过度设计。

ArkTS 类型系统的适应: 从 TypeScript 迁移到 ArkTS 时,最大的心理转变是接受"显式优于隐式"。虽然多写了一些类型标注,但换来了编译时的类型安全保障和更清晰的代码意图表达。在本应用中,所有接口字段、方法参数、局部变量都标注了具体类型,没有使用 any 或 unknown 类型,这使得代码在 IDE 中具有完整的智能提示和自动补全支持。

UI 状态管理的三层模型: 本应用将 UI 状态分为三个层次:用户输入状态(选择器选中值)、处理状态(isLoading)、结果状态(outputData、hasResult)。这三层状态之间通过 onGenerate() 方法串联,形成了清晰的"输入 → 处理 → 输出"数据流。这种分层模型适用于所有类似的三段式交互应用,可以作为 40 个应用开发的通用模板。


七、Prompt 工程深度分析

7.1 Prompt 设计的结构化思维

「衣品进化室」的 Prompt 模板体现了结构化 Prompt 设计的核心原则。让我们逐层分析其设计逻辑:

系统指令层——角色边界定义: Prompt 开头使用 # 系统指令 明确角色定位:“你是一个’每日穿搭方案’数据生成器”。这个角色定义有几个关键作用:限定 AI 只输出穿搭方案,不偏离到其他话题;设定行为边界(“只返回符合下方 JSON Schema 的数据,禁止输出任何解释、问候、免责声明”),确保输出格式可控;建立专业权威性(“搭配需符合所选气温与场合,reason 说明搭配逻辑”),要求 AI 给出有理有据的建议。

用户输入层——结构化参数提取: 用户输入被设计为结构化 JSON,包含 temperature、occasion、style 三个字段。这种结构化设计使得前端可以轻松地将用户在下拉选择器中的选择转换为 API 请求体,也使得 AI 能够精确理解每个参数的含义,避免自然语言输入的歧义。

输出格式层——JSON Schema 约束: 输出格式被明确指定为 JSON 结构,包含 top、bottom、shoes、accessory、reason、alt 六个字段。这种严格的 Schema 约束确保了 AI 输出的可解析性——前端可以直接将 JSON 解析为 OutfitOutput 接口的实例,不需要额外的文本清洗或格式转换。

兜底规则层——容错机制: Prompt 末尾的兜底规则(“信息不足时按通勤+简约+20℃给默认方案”)提供了输入不完整时的容错处理。这个规则与代码中的 else 分支(通用搭配方案)形成了前后呼应——即使 Prompt 层面和代码层面都设计了兜底策略,确保用户在任何情况下都能获得可用结果。

7.2 Temperature 参数的选择逻辑

temperature=0.4 的设置是一个经过深思熟虑的选择。在 AI 生成领域,temperature 控制输出的随机性和创造性:取值越低(接近 0),输出越确定和保守;取值越高(接近 1),输出越随机和有创意。穿搭建议这个场景的特殊性在于:

  • 需要一定的创意性:同一个场合和风格下,应该有多种合理的穿搭方案,而非千篇一律
  • 但不能过于随机:穿搭方案必须符合基本的美学原则和实用逻辑,不能出现"短袖配羽绒服"这样的荒谬组合
  • 需要逻辑一致性:reason 字段必须基于气温、场合、风格给出合理解释,过高的 temperature 会破坏这种逻辑一致性

0.4 是一个在"创意"和"确定性"之间的平衡点,既能产生多样化的穿搭建议,又能保持建议的合理性和逻辑性。

7.3 与后续 Apps 的 Prompt 设计对比

将「衣品进化室」的 Prompt 与「冰箱魔法厨」(App32)和「营养方程式」(App33)进行对比,可以发现 Prompt 设计的一些共性规律:

维度 衣品进化室 冰箱魔法厨 营养方程式
输入维度 3 个枚举值 数组 + 枚举 + 数字 3 数字 + 枚举 + 数组
输出复杂度 1 套搭配 2 道菜谱 3 餐 + 宏量分配
Temperature 0.4 0.4 0.3
兜底策略 默认场景 通用菜谱 维持体重估算
可核验性 基于材质/版型 基于食材/做法 基于热量公式

共性规律是:Temperature 设置与输出的"可核验性"负相关——输出越需要精确计算(如营养热量),temperature 越低;输出越需要创意发挥(如起名),temperature 越高。穿搭建议处于两者之间,因此选择了 0.4 的中间值。

7.4 前端与 Prompt 的协同设计

「衣品进化室」的代码实现与 Prompt 模板之间存在紧密的协同关系,这种协同设计是保证 AI 应用质量的关键:

参数映射的一致性: 前端下拉选择器中的选项值与 Prompt 中用户输入 Schema 的枚举值完全一致。例如,气温选择器中的 ‘20-25℃’ 与 Prompt 中的 “temperature”: “20-25℃” 使用的是完全相同的字符串值。这种一致性确保了前端参数可以直接映射到 API 请求体,不需要额外的转换逻辑。

输出结构的类型对应: ArkTS 中的 OutfitItem 和 OutfitOutput 接口定义与 Prompt 中的输出 JSON Schema 字段一一对应。top、bottom、shoes、accessory、reason、alt 六个字段在接口定义和 Prompt 输出格式中完全一致,这使得前端可以安全地将 API 响应的 JSON 数据直接赋值给类型安全的接口实例。

兜底策略的双重保障: 代码中的 else 分支(通用百搭方案)与 Prompt 中的兜底规则(“信息不足时按通勤+简约+20℃给默认方案”)形成了双层容错。第一层在 Prompt 层面——当用户输入参数不完整时,AI 会自动使用默认值;第二层在代码层面——当场景匹配失败时,代码会返回内置的通用 Mock 数据。这种双重保障确保了用户在任何情况下都能获得可用的穿搭建议。

可扩展性的预留设计: 代码中的 callAIAPI() 方法(注释形式)已经预留了完整的 API 调用结构,包括请求方法(POST)、请求头(Content-Type: application/json)、请求体结构(temperature、occasion、style 三个字段)以及响应解析逻辑。当需要对接真实大模型 API 时,只需取消注释并替换 API 端点 URL 即可,无需修改任何其他代码。


本文档基于「衣品进化室」应用的完整开发实践撰写,所有代码示例均来自实际项目代码。

Logo

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

更多推荐