基于HarmonyOS的AI合同审查应用开发——从对齐到评估的全流程技术实践
基于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关键字声明数据类,所有字段显式声明类型并初始化。
这种分层设计的优势在于:
- 关注点分离:UI渲染与业务逻辑解耦,Page层只关心视图状态,不关心数据如何生成。
- 可测试性:Service层可以独立于UI进行单元测试。
- 可替换性:当从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 边界确认
在MVP(最小可行产品)阶段,我们明确了以下边界:
- 当前范围:实现UI界面、数据模型和Mock数据处理流程,验证产品概念的可行性。
- 后续扩展:对接真实AI模型(如接入大语言模型API),实现真正的智能合同审查。
- 不在范围:合同模板管理、多用户协作、合同版本对比等高级功能留待后续版本。
1.3 技术规范对齐
在开始编码之前,需要严格对齐ArkTS的语法约束。ArkTS是HarmonyOS原生应用开发语言,它在TypeScript基础上进行了精简和约束,以提升运行时性能和类型安全性。以下是在开发过程中需要特别关注的规则:
ArkTS语法约束要点:
-
不支持
any和unknown类型:所有变量必须显式指定类型。这意味着开发者必须清楚每个变量的具体类型,不能依赖动态类型来"绕过"编译器检查。在实际编码中,我们使用Record<string, Object>来替代any作为字典类型,使用具体的类类型(如AI合同审查Data)来替代unknown。 -
不支持解构赋值:必须使用临时变量逐字段操作。例如,不能写
const { name, age } = obj,而必须写let name = obj.name; let age = obj.age。这虽然增加了代码量,但使数据来源更加清晰可追踪。 -
不支持
for...in遍历对象:数组使用常规for循环或ForEach组件。在ArkUI中,ForEach组件是遍历数组并渲染列表的首选方式,它提供了高效的列表渲染和键值管理机制。 -
不支持索引签名:使用数组替代。在TypeScript中常见的
[key: string]: any模式在ArkTS中不被允许,需要使用Record<string, T>或具体的数据结构。 -
所有
import语句必须在文件开头:不能与其他语句交错。这是ArkTS的强制性要求,编译器会检查所有import语句是否位于模块的最顶部。 -
不支持
Function.bind、Function.apply和Function.call:遵循传统OOP的this语义。在ArkTS中,this只能在实例方法中使用,独立函数和静态方法中不能使用this。 -
不支持对象字面量作为类型声明:必须显式声明类和接口。这意味着不能直接使用
let obj: { name: string } = { name: 'test' },而必须先声明一个类或接口。 -
属性访问必须使用点语法:不支持
obj["field"]索引访问。所有对象属性必须通过点语法访问,如obj.fieldName。标准库中的类型化数组(如Int32Array)是例外。 -
不支持展开运算符用于对象:展开运算符仅支持将数组或派生自数组的类展开到rest参数或数组字面量中。对于对象数据,需要手动逐字段复制。
-
不支持
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渲染
具体流程如下:
- 用户在
TextInput组件中输入合同文本和合同类型。 onChange回调将输入值同步到inputData对象。- 用户点击"签发签证"按钮,触发
onClick事件。 onClick中调用service.generateData(inputData)生成审查结果。- 返回的
AI合同审查Data对象赋值给resultData。 showResult设置为true,触发条件渲染。- 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类型,所有的异常处理需要更加谨慎:
- 输入验证:在Service层对输入参数进行合法性检查,确保类型正确。
- 空值处理:所有Model字段使用空字符串或空数组作为默认值,避免空指针异常。
- UI层保护:使用条件渲染(
if语句)确保在数据未准备好时不会渲染结果区域。 - 类型转换安全:在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类包含三个维度的数据:
-
风险分析维度:
risk_clauses(风险条款列表)、clause(当前条款)、risk(风险描述)、level(风险等级)、suggestion(修改建议)、rewrite(改写方案)。这些字段共同构成完整的风险分析报告。 -
合规检查维度:
compliance_check(合规检查项列表)、item(检查项)、status(合规状态)、note(备注)。这些字段用于评估合同条款的法律合规性。 -
整体评估维度:
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
}
}
设计决策说明:
-
为什么
generateData返回新实例而非修改内部model? 这是为了保持Service的无状态性。每次调用generateData都返回全新的AI合同审查Data实例,避免了状态污染和副作用。这在后续对接真实AI API时尤为重要——每个请求都应该独立生成结果。 -
为什么使用
Record<string, Object>作为输入类型? 在ArkTS中,不支持any类型,而Page层使用Record<string, Object>存储表单输入。Service层保持相同的签名以保持类型兼容。在实际使用时,通过String()进行安全的向下转型。 -
Mock数据策略:当前使用硬编码的示例数据(如
['示例数据1', '示例数据2', '示例数据3'])和拼接输入文本的占位结果。这种策略在MVP阶段能够快速验证UI交互流程,后续替换为真实AI模型时只需修改generateData方法的内部实现。
任务3:页面视图实现(Page层 - UI布局)
文件:entry/src/main/ets/apps/AI合同审查/AI合同审查Page.ets
任务描述:实现合同审查页面的UI布局,包括头部、输入区和按钮。
技术要点:
- 使用
@Component装饰器声明组件 - 使用
@State装饰器声明响应式状态变量 - 使用
Column、Row、Scroll等布局组件构建页面结构 - 使用
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设计理念:
该应用采用了"护照签证"的视觉隐喻——用户输入合同文本如同填写签证申请表,点击"签发签证"按钮后获得审查结果,结果展示区域设计为"签证页"风格。这种设计语言的优点包括:
-
降低认知负担:护照签证是用户熟悉的日常概念,将抽象的数据处理过程映射为具体的"签证申请-签发"流程,用户可以直观理解操作步骤。无需额外的使用说明,用户看到界面就能自然理解"输入信息→提交申请→获取结果"的操作路径。
-
增强情感体验:复古护照风格的UI设计(棕色系配色、国徽装饰、VISA标签)营造了正式、权威的产品调性,与"合同审查"这一严肃场景高度契合。配色方案上,主色调采用
#8B4513(棕色)和#D4A574(金色),背景色使用#F5E6D3(米色),整体呈现复古、典雅的视觉感受。按钮使用金色文字配棕色背景(color: '#FFD700', backgroundColor: '#8B4513'),模拟了护照印章的视觉效果。 -
提升品牌识别度:统一的视觉风格使应用在多个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语句实现。需要注意以下几点:
- 空值保护:始终在渲染前检查数组或对象是否为
null,避免运行时错误。例如:if (this.resultData.risk_clauses)。 ForEach的键值生成:ForEach的第三个参数是键值生成函数,必须为每个列表项生成唯一标识。使用index.toString()是最简单的方式,但如果列表会动态变化,建议使用更稳定的标识。- 响应式更新:
@State变量的变化会自动触发UI重渲染。当showResult从false变为true时,结果区域会立即显示,无需手动操作DOM。
任务5:集成与联调
任务描述:将Model、Se
更多推荐

所有评论(0)