基于HarmonyOS的AI合同审查应用开发——从对齐到评估的全流程技术实践

摘要:本文以"AI合同审查"应用的完整开发过程为例,详细阐述如何在HarmonyOS生态下,利用ArkTS语言和ArkUI框架,构建一个面向商务人士和法务工作者的AI驱动的合同智能审查工具。文章按照对齐(Align)、架构(Architect)、原子化(Atomize)、审批(Approve)、自动化执行(Automate)、评估(Assess)六个阶段,系统性呈现从需求分析到代码实现再到回顾总结的全链路技术实践。全文包含完整的ArkTS代码片段、架构设计图、数据流分析以及工程化实践思考,共计约10000字。


在这里插入图片描述

一、对齐阶段(Align)

1.1 项目上下文分析

1.1.1 项目背景与技术栈概览

"AI合同审查"是运行在HarmonyOS操作系统上的AI应用子项目之一,隶属于一个大型应用集合。该项目旨在帮助用户快速审查合同文本,识别潜在风险条款,提供合规性检查和修改建议,从而降低商业合同签署中的法律风险。

该项目基于以下技术栈构建:

  • 编程语言:ArkTS(HarmonyOS的扩展TypeScript方言,遵循严格的静态类型约束)
  • UI框架:ArkUI(声明式UI框架,以@Component@State为核心驱动)
  • 构建工具:Hvigor(HarmonyOS专属构建工具,基于build-profile.json5配置)
  • 目标设备:手机(deviceTypes: ["phone"]
  • API级别:HarmonyOS API(基于@kit.ArkUI等Kit级SDK)
  • IDE:DevEco Studio
  • 无第三方依赖:当前项目采用零外部依赖策略,所有功能均基于HarmonyOS原生API实现

项目根目录位于 c:\Users\l\DevEcoStudioProjects\MyApplication,模块结构如下:

MyApplication/
├── AppScope/                    # 应用全局配置
│   ├── app.json5
│   └── resources/
├── entry/                       # 主entry模块
│   ├── oh-package.json5         # 模块依赖声明(当前无第三方依赖)
│   ├── build-profile.json5      # 模块构建配置
│   └── src/main/
│       ├── module.json5         # 模块清单(权限、Ability配置)
│       ├── resources/           # 国际化资源、颜色、配置
│       │   ├── base/element/    # 基础资源(字符串、颜色、浮点数)
│       │   ├── dark/element/    # 深色主题资源
│       │   └── rawfile/apps/    # 应用列表数据源(apps.json)
│       └── ets/
│           ├── entryability/    # Ability入口
│           ├── pages/           # 首页(Index.ets)
│           └── apps/            # AI应用,每个独立目录
│               └── AI合同审查/
│                   ├── AI合同审查Page.ets     # 视图层(UI交互)
│                   ├── AI合同审查Model.ets    # 数据模型层
│                   └── AI合同审查Service.ets  # 服务层(业务逻辑)
1.1.2 架构模式分析

整个AI应用矩阵采用三层架构模式(Model-Service-Page),这与经典的MVVM模式高度一致:

  • Page层(视图层):负责UI展示和用户交互,使用ArkUI的@Component装饰器声明组件,通过@State装饰器管理响应式状态。用户输入通过TextInput组件收集,结果通过条件渲染展示。
  • Service层(服务层):封装业务逻辑,处理数据生成和转换。当前阶段使用Mock数据模拟AI输出,后续可对接真实AI模型API。
  • Model层(数据模型层):定义数据结构,使用ArkTS的class关键字声明数据类,所有字段显式声明类型并初始化。

这种分层设计的优势在于:

  1. 关注点分离:UI渲染与业务逻辑解耦,Page层只关心视图状态,不关心数据如何生成。
  2. 可测试性:Service层可以独立于UI进行单元测试。
  3. 可替换性:当从Mock数据切换到真实AI API时,只需修改Service层,Page层和Model层无需改动。
1.1.3 应用入口与路由机制

在HarmonyOS中,页面路由通过main_pages.json配置。该文件位于 entry/src/main/resources/base/profile/main_pages.json,声明了所有可路由的页面路径:

{
  "src": [
    "pages/Index",
    "apps/AI合同审查/AI合同审查Page",
    // ... 其他应用页面
  ]
}

首页(Index.ets)从 rawfile/apps/apps.json 中读取应用列表,每个应用条目包含图标、标题、颜色主题和页面路径。当用户点击某个应用卡片时,通过 router.pushUrl() 导航到对应页面:

// Index.ets 中的导航逻辑
.onClick(() => {
  router.pushUrl({ url: app.pageUrl })
})

这种集中式路由配置+动态数据驱动的设计,使得新增一个AI应用只需在 apps.json 中添加一条记录,并在 main_pages.json 中注册页面路径,完全无需修改首页代码,体现了良好的可扩展性。

1.2 需求理解与确认

1.2.1 原始需求

"AI合同审查"应用的原始需求可以概括为:用户输入合同文本和合同类型,系统自动分析合同内容,识别风险条款,进行合规性检查,并给出修改建议和总体风险评估。

1.2.2 需求细化

经过分析,我们将需求拆解为以下核心功能点:

  1. 合同文本输入:用户可以通过文本输入框输入待审查的合同内容。
  2. 合同类型选择:用户指定合同类型(如买卖合同、租赁合同、劳动合同等),以便系统进行针对性审查。
  3. 风险条款识别:自动识别合同中的高风险条款,列出具体风险项。
  4. 合规性检查:检查合同条款是否符合相关法律法规要求。
  5. 缺失条款检测:识别合同中缺失的关键条款,提示用户补充。
  6. 修改建议生成:对有风险的条款提供改写建议。
  7. 总体风险评估:给出合同的整体风险等级和最终建议。
1.2.3 边界确认

在MVP(最小可行产品)阶段,我们明确了以下边界:

  • 当前范围:实现UI界面、数据模型和Mock数据处理流程,验证产品概念的可行性。
  • 后续扩展:对接真实AI模型(如接入大语言模型API),实现真正的智能合同审查。
  • 不在范围:合同模板管理、多用户协作、合同版本对比等高级功能留待后续版本。

1.3 技术规范对齐

在开始编码之前,需要严格对齐ArkTS的语法约束。ArkTS是HarmonyOS原生应用开发语言,它在TypeScript基础上进行了精简和约束,以提升运行时性能和类型安全性。以下是在开发过程中需要特别关注的规则:

ArkTS语法约束要点

  1. 不支持anyunknown类型:所有变量必须显式指定类型。这意味着开发者必须清楚每个变量的具体类型,不能依赖动态类型来"绕过"编译器检查。在实际编码中,我们使用Record<string, Object>来替代any作为字典类型,使用具体的类类型(如AI合同审查Data)来替代unknown

  2. 不支持解构赋值:必须使用临时变量逐字段操作。例如,不能写const { name, age } = obj,而必须写let name = obj.name; let age = obj.age。这虽然增加了代码量,但使数据来源更加清晰可追踪。

  3. 不支持for...in遍历对象:数组使用常规for循环或ForEach组件。在ArkUI中,ForEach组件是遍历数组并渲染列表的首选方式,它提供了高效的列表渲染和键值管理机制。

  4. 不支持索引签名:使用数组替代。在TypeScript中常见的[key: string]: any模式在ArkTS中不被允许,需要使用Record<string, T>或具体的数据结构。

  5. 所有import语句必须在文件开头:不能与其他语句交错。这是ArkTS的强制性要求,编译器会检查所有import语句是否位于模块的最顶部。

  6. 不支持Function.bindFunction.applyFunction.call:遵循传统OOP的this语义。在ArkTS中,this只能在实例方法中使用,独立函数和静态方法中不能使用this

  7. 不支持对象字面量作为类型声明:必须显式声明类和接口。这意味着不能直接使用let obj: { name: string } = { name: 'test' },而必须先声明一个类或接口。

  8. 属性访问必须使用点语法:不支持obj["field"]索引访问。所有对象属性必须通过点语法访问,如obj.fieldName。标准库中的类型化数组(如Int32Array)是例外。

  9. 不支持展开运算符用于对象:展开运算符仅支持将数组或派生自数组的类展开到rest参数或数组字面量中。对于对象数据,需要手动逐字段复制。

  10. 不支持in运算符:要检查对象是否包含某个属性,应使用instanceof替代。这是因为在ArkTS中,对象布局在编译时已知且运行时不可更改。

这些约束在后续的代码编写中需要时刻遵守,否则将导致编译失败。对于从TypeScript转过来的开发者来说,需要特别注意这些差异点,在编码过程中逐步适应ArkTS的静态类型范式。


二、架构阶段(Architect)

2.1 整体架构设计

基于对齐阶段的成果,我们设计了三层架构,各层职责明确、接口清晰。

2.1.1 架构层次图
┌─────────────────────────────────────────────────────────┐
│                    Page层(视图层)                       │
│  AI合同审查Page.ets                                      │
│  ┌──────────────────────────────────────────────────┐   │
│  │  @Component AI合同审查Page                        │   │
│  │  ├── @State inputData: 用户输入数据               │   │
│  │  ├── @State resultData: 审查结果数据              │   │
│  │  ├── @State showResult: 结果展示开关              │   │
│  │  └── build() 方法                                  │   │
│  │      ├── Header(护照风格标题栏)                   │   │
│  │      ├── 输入区(合同文本 + 合同类型)              │   │
│  │      ├── 按钮(触发审查)                          │   │
│  │      └── 结果区(条件渲染展示审查结果)             │   │
│  └──────────────────────────────────────────────────┘   │
├─────────────────────────────────────────────────────────┤
│                    Service层(服务层)                    │
│  AI合同审查Service.ets                                  │
│  ┌──────────────────────────────────────────────────┐   │
│  │  @class AI合同审查Service                        │   │
│  │  ├── generateData(input): 生成审查数据            │   │
│  │  └── 内部Mock数据生成逻辑                         │   │
│  └──────────────────────────────────────────────────┘   │
├─────────────────────────────────────────────────────────┤
│                    Model层(数据模型层)                  │
│  AI合同审查Model.ets                                    │
│  ┌──────────────────────────────────────────────────┐   │
│  │  @class AI合同审查Data                           │   │
│  │  ├── risk_clauses: string[] 风险条款列表          │   │
│  │  ├── clause: string 当前条款                      │   │
│  │  ├── risk: string 风险描述                        │   │
│  │  ├── level: string 风险等级                       │   │
│  │  ├── suggestion: string 修改建议                  │   │
│  │  ├── rewrite: string 改写方案                     │   │
│  │  ├── compliance_check: string[] 合规检查项        │   │
│  │  ├── item: string 检查项                          │   │
│  │  ├── status: string 合规状态                      │   │
│  │  ├── note: string 备注                            │   │
│  │  ├── missing_clauses: string[] 缺失条款           │   │
│  │  ├── overall_risk: string 总体风险                 │   │
│  │  └── recommendation: string 最终建议              │   │
│  └──────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────┘
2.1.2 数据流设计

整个应用的数据流遵循"单向数据流"原则:

用户输入 → inputData (@State) → Service.generateData() → resultData (@State) → UI渲染

具体流程如下:

  1. 用户在TextInput组件中输入合同文本和合同类型。
  2. onChange回调将输入值同步到inputData对象。
  3. 用户点击"签发签证"按钮,触发onClick事件。
  4. onClick中调用service.generateData(inputData)生成审查结果。
  5. 返回的AI合同审查Data对象赋值给resultData
  6. showResult设置为true,触发条件渲染。
  7. ArkUI的响应式系统自动更新UI,展示审查结果。

这种单向数据流的优势在于:数据变化可预测、状态管理简单、调试方便。

2.2 模块依赖关系

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,符合分层架构的依赖倒置原则。Model层不依赖任何其他模块,保证了数据结构的独立性。

2.3 接口契约定义

Service层对外暴露的核心接口:

// 输入:用户输入的合同数据
interface InputData {
  contract_text: string  // 合同文本
  contract_type: string  // 合同类型
}

// 输出:AI审查结果
interface OutputData {
  risk_clauses: string[]      // 风险条款列表
  clause: string              // 当前分析的条款
  risk: string                // 风险描述
  level: string               // 风险等级
  suggestion: string          // 修改建议
  rewrite: string             // 改写方案
  compliance_check: string[]  // 合规检查项列表
  item: string               // 检查项
  status: string             // 合规状态
  note: string               // 备注
  missing_clauses: string[]  // 缺失条款列表
  overall_risk: string       // 总体风险
  recommendation: string     // 最终建议
}

2.4 异常处理策略

在ArkTS中,由于不支持any类型,所有的异常处理需要更加谨慎:

  1. 输入验证:在Service层对输入参数进行合法性检查,确保类型正确。
  2. 空值处理:所有Model字段使用空字符串或空数组作为默认值,避免空指针异常。
  3. UI层保护:使用条件渲染(if语句)确保在数据未准备好时不会渲染结果区域。
  4. 类型转换安全:在Service层使用String()构造函数进行显式类型转换,确保输入数据始终是字符串类型。

三、原子化阶段(Atomize)

在原子化阶段,我们将整个开发任务分解为5个可独立执行、可验证的原子任务。

任务1:数据模型定义(Model层)

文件entry/src/main/ets/apps/AI合同审查/AI合同审查Model.ets

任务描述:定义AI合同审查Data类,包含所有审查结果字段。

技术要点

  • 所有字段必须显式声明类型(ArkTS不支持类型推断)
  • 所有字段必须在类声明中直接初始化(不支持在构造函数中声明)
  • 使用string[]数组类型存储列表数据
  • 构造函数中可再次初始化以确保默认值

代码实现

export class AI合同审查Data {
  risk_clauses: string[] = []
  clause: string = ''
  risk: string = ''
  level: string = ''
  suggestion: string = ''
  rewrite: string = ''
  compliance_check: string[] = []
  item: string = ''
  status: string = ''
  note: string = ''
  missing_clauses: string[] = []
  overall_risk: string = ''
  recommendation: string = ''

  constructor() {
    this.risk_clauses = []
    this.clause = ''
    this.risk = ''
    this.level = ''
    this.suggestion = ''
    this.rewrite = ''
    this.compliance_check = []
    this.item = ''
    this.status = ''
    this.note = ''
    this.missing_clauses = []
    this.overall_risk = ''
    this.recommendation = ''
  }
}

设计考量

数据模型的设计直接决定了应用的能力边界。AI合同审查Data类包含三个维度的数据:

  1. 风险分析维度risk_clauses(风险条款列表)、clause(当前条款)、risk(风险描述)、level(风险等级)、suggestion(修改建议)、rewrite(改写方案)。这些字段共同构成完整的风险分析报告。

  2. 合规检查维度compliance_check(合规检查项列表)、item(检查项)、status(合规状态)、note(备注)。这些字段用于评估合同条款的法律合规性。

  3. 整体评估维度missing_clauses(缺失条款列表)、overall_risk(总体风险)、recommendation(最终建议)。这些字段提供合同的整体评估概览。

这种三维度设计使得审查结果既包含微观的条款级分析,也包含宏观的整体评估,信息层次丰富。

任务2:服务层实现(Service层)

文件entry/src/main/ets/apps/AI合同审查/AI合同审查Service.ets

任务描述:实现AI合同审查Service类,封装业务逻辑,当前阶段使用Mock数据模拟AI输出。

技术要点

  • 使用class关键字声明服务类
  • Service层持有Model实例,但直接返回新的Model实例而非修改内部状态
  • 输入参数使用Record<string, Object>类型以匹配Page层的数据结构
  • 使用String()进行安全的类型转换

代码实现

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 contract_textVal: string = String(input['contract_text'] || '')

    result.risk_clauses = ['示例数据1', '示例数据2', '示例数据3']
    result.compliance_check = ['示例数据1', '示例数据2', '示例数据3']
    result.missing_clauses = ['示例项1', '示例项2', '示例项3']
    result.overall_risk = '生成结果:' + contract_textVal
    result.recommendation = '生成结果:' + contract_textVal
    return result
  }
}

设计决策说明

  1. 为什么generateData返回新实例而非修改内部model? 这是为了保持Service的无状态性。每次调用generateData都返回全新的AI合同审查Data实例,避免了状态污染和副作用。这在后续对接真实AI API时尤为重要——每个请求都应该独立生成结果。

  2. 为什么使用Record<string, Object>作为输入类型? 在ArkTS中,不支持any类型,而Page层使用Record<string, Object>存储表单输入。Service层保持相同的签名以保持类型兼容。在实际使用时,通过String()进行安全的向下转型。

  3. Mock数据策略:当前使用硬编码的示例数据(如['示例数据1', '示例数据2', '示例数据3'])和拼接输入文本的占位结果。这种策略在MVP阶段能够快速验证UI交互流程,后续替换为真实AI模型时只需修改generateData方法的内部实现。

任务3:页面视图实现(Page层 - UI布局)

文件entry/src/main/ets/apps/AI合同审查/AI合同审查Page.ets

任务描述:实现合同审查页面的UI布局,包括头部、输入区和按钮。

技术要点

  • 使用@Component装饰器声明组件
  • 使用@State装饰器声明响应式状态变量
  • 使用ColumnRowScroll等布局组件构建页面结构
  • 使用TextInput组件收集用户输入
  • 使用Button组件触发审查操作

关键代码片段

@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() {
      // 护照封面Header
      Row() {
        Text('← 返回')
          .fontSize(13)
          .fontColor('#8B4513')
          .onClick(() => { router.back() })
        Blank()
        Column() {
          Text('📱 AI合同审查')
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .fontColor('#1A1A2E')
          Text('PASSPORT · 语言签证')
            .fontSize(9)
            .fontColor('#8B4513')
            .letterSpacing(2)
            .margin({ top: 2 })
        }
        Blank()
        Text('VISA')
          .fontSize(11)
          .fontColor('#D4A574')
          .fontWeight(FontWeight.Bold)
          .border({ width: 1, color: '#D4A574', radius: 4 })
          .padding({ left: 8, right: 8, top: 3, bottom: 3 })
      }
      .width('100%')
      .padding({ left: 20, right: 20, top: 16, bottom: 14 })
      .backgroundColor('#F5E6D3')

      Scroll() {
        Column() {
          // ... 输入区域和结果区域
        }
      }
    }
  }
}

UI设计理念

该应用采用了"护照签证"的视觉隐喻——用户输入合同文本如同填写签证申请表,点击"签发签证"按钮后获得审查结果,结果展示区域设计为"签证页"风格。这种设计语言的优点包括:

  1. 降低认知负担:护照签证是用户熟悉的日常概念,将抽象的数据处理过程映射为具体的"签证申请-签发"流程,用户可以直观理解操作步骤。无需额外的使用说明,用户看到界面就能自然理解"输入信息→提交申请→获取结果"的操作路径。

  2. 增强情感体验:复古护照风格的UI设计(棕色系配色、国徽装饰、VISA标签)营造了正式、权威的产品调性,与"合同审查"这一严肃场景高度契合。配色方案上,主色调采用#8B4513(棕色)和#D4A574(金色),背景色使用#F5E6D3(米色),整体呈现复古、典雅的视觉感受。按钮使用金色文字配棕色背景(color: '#FFD700', backgroundColor: '#8B4513'),模拟了护照印章的视觉效果。

  3. 提升品牌识别度:统一的视觉风格使应用在多个AI应用中具有高辨识度。在应用矩阵中,每个AI应用都有独特的视觉主题,而"护照签证"风格让"AI合同审查"在众多应用中脱颖而出,用户可以在首页卡片列表中快速识别。

ArkUI样式的链式调用机制

在ArkUI中,组件样式的设置采用链式调用(Builder Pattern)的方式。每个方法调用都返回组件本身,从而支持连续设置多个属性。这种设计模式在声明式UI框架中非常常见,具有以下优点:

  • 代码简洁:无需重复写组件名称,所有样式设置串联在一起。
  • 可读性强:从上到下依次阅读,可以直观地了解组件的完整样式。
  • 类型安全:每个属性设置方法都有明确的类型定义,IDE可以提供自动补全和类型检查。

例如,在Header中的"VISA"标签使用了多层样式链:

Text('VISA')
  .fontSize(11)
  .fontColor('#D4A574')
  .fontWeight(FontWeight.Bold)
  .border({ width: 1, color: '#D4A574', radius: 4 })
  .padding({ left: 8, right: 8, top: 3, bottom: 3 })

这行代码依次设置了字体大小、字体颜色、字重、边框样式和内边距,最终呈现出一个精致的标签样式。

ArkUI布局组件的选择策略

在页面布局中,我们使用了以下ArkUI布局组件:

  • Column:垂直布局容器,子组件从上到下排列。用于页面整体结构和各个区块的垂直排列。
  • Row:水平布局容器,子组件从左到右排列。用于Header中的左右布局(返回按钮-标题-VISA标签)。
  • Scroll:可滚动容器,当内容超出屏幕高度时提供滚动能力。整个页面内容包裹在Scroll中,确保在手机屏幕上可以完整查看所有内容。
  • Blank:弹性空白填充组件,在Row中自动占据剩余空间,实现两端对齐效果。
  • Flex:弹性布局容器,配合wrap: FlexWrap.Wrap实现网格布局(在首页Index.ets中使用)。

任务4:页面视图实现(Page层 - 结果展示)

任务描述:实现审查结果的展示逻辑,使用条件渲染和列表渲染。

技术要点

  • 使用if条件渲染控制结果区域的显示/隐藏
  • 使用ForEach组件遍历数组数据
  • 使用@State驱动的响应式更新

关键代码片段

// 结果区域 - 签证页
if (this.showResult && this.resultData !== null) {
  Column() {
    Text('✓ 签证已签发')
      .fontSize(14)
      .fontWeight(FontWeight.Bold)
      .fontColor('#2E7D32')
      .margin({ bottom: 14 })

    // 风险条款列表
    Text('Risk clauses')
      .fontSize(13)
      .fontWeight(FontWeight.Bold)
      .fontColor('#333333')
      .margin({ top: 10, bottom: 6 })
    if (this.resultData.risk_clauses) {
      ForEach(this.resultData.risk_clauses, (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('Clause: ')
        .fontSize(12)
        .fontWeight(FontWeight.Medium)
        .fontColor('#666666')
      Text(this.resultData.clause)
        .fontSize(12)
        .fontColor('#333333')
    }
    // ... 更多详情字段

    // 合规检查列表
    Text('Compliance check')
      .fontSize(13)
      .fontWeight(FontWeight.Bold)
      .fontColor('#333333')
      .margin({ top: 10, bottom: 6 })
    if (this.resultData.compliance_check) {
      ForEach(this.resultData.compliance_check, (item: string, index: number) => {
        // ... 列表项渲染
      }, (item: string, index: number) => index.toString())
    }

    // 缺失条款列表
    Text('Missing clauses')
      .fontSize(13)
      .fontWeight(FontWeight.Bold)
      .fontColor('#333333')
      .margin({ top: 10, bottom: 6 })
    if (this.resultData.missing_clauses) {
      ForEach(this.resultData.missing_clauses, (item: string, index: number) => {
        // ... 列表项渲染
      }, (item: string, index: number) => index.toString())
    }

    // 总体评估
    Row() {
      Text('Overall risk: ')
        .fontSize(12)
        .fontWeight(FontWeight.Medium)
        .fontColor('#666666')
      Text(this.resultData.overall_risk)
        .fontSize(12)
        .fontColor('#333333')
    }
    Row() {
      Text('Recommendation: ')
        .fontSize(12)
        .fontWeight(FontWeight.Medium)
        .fontColor('#666666')
      Text(this.resultData.recommendation)
        .fontSize(12)
        .fontColor('#333333')
    }
  }
  .width('100%')
  .padding(18)
  .backgroundColor('#FFF8F0')
  .border({ width: 2, color: '#D4A574', radius: 8 })
  .margin({ bottom: 20 })
}

ArkTS中的条件渲染最佳实践

在ArkUI中,条件渲染通过if语句实现。需要注意以下几点:

  1. 空值保护:始终在渲染前检查数组或对象是否为null,避免运行时错误。例如:if (this.resultData.risk_clauses)
  2. ForEach的键值生成ForEach的第三个参数是键值生成函数,必须为每个列表项生成唯一标识。使用index.toString()是最简单的方式,但如果列表会动态变化,建议使用更稳定的标识。
  3. 响应式更新@State变量的变化会自动触发UI重渲染。当showResultfalse变为true时,结果区域会立即显示,无需手动操作DOM。

任务5:集成与联调

任务描述:将Model、Se

Logo

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

更多推荐