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

目录

  1. 前言与背景
  2. 项目架构设计
  3. 开发环境与工程配置
  4. Stage 模型与 Ability 生命周期
  5. ArkUI 声明式 UI 开发
  6. 状态管理与表单设计
  7. 模板引擎的设计与实现
  8. @Builder 组件化实践
  9. ArkTS 严格模式与常见错误修复
  10. UI/UX 设计:纸笔风格视觉系统
  11. 条件渲染与页面切换
  12. 鸿蒙 API 24 特性应用
  13. 安全与实践考虑
  14. 踩坑与经验总结
  15. 结语与展望

1. 前言与背景

1.1 为什么做检讨书生成器?

考试是教育体系中不可或缺的环节,而有考试的地方,就难免有作弊行为。根据教育部近年来的统计数据,每年高校考试中因作弊受处分的学生数量数以万计。作弊被发现后,学生通常需要提交一份书面检讨书作为认识错误、表明态度的依据。

然而,写检讨书对于很多学生来说是一件困难的事情——不是因为不想认错,而是不知道如何组织语言、如何表达悔过之心、如何写出有诚意的检讨。网络上流传的各种检讨书模板要么过于死板,要么不够全面,学生往往需要在多个模板之间拼凑,费时费力。

这就是我们开发"考试作弊检讨书生成器"的初衷——通过智能模板组合,帮助学生快速生成结构完整、语气得体、内容诚恳的检讨书。同时,通过提供多种语气风格,让学生在认错的同时也能保有自己的个性。

1.2 为什么选择鸿蒙 Next?

鸿蒙 Next 系统从底层开始全面自研,带来了几个关键优势:

  • ArkUI 声明式框架:响应式 UI 开发,状态驱动视图更新,非常适合表单类应用
  • ArkTS 强类型语言:基于 TypeScript 的强类型方言,提供编译时类型检查,减少运行时错误
  • Stage 模型:清晰的生命周期管理,适合单页面富交互应用
  • API 24 成熟度高:SDK 6.1.1 版本提供了完善的 UI 组件和工具链
  • 编译期优化:ArkTS 编译器在构建时进行类型检查和代码优化,提前发现潜在问题

1.3 本文的目标读者

  • 有鸿蒙 ArkUI 基础,想了解完整项目实践的开发者
  • 从 TypeScript/React 转向鸿蒙 ArkTS 开发的工程师
  • 对文本生成、模板引擎实现感兴趣的开发者
  • 正在学习 ArkTS 严格模式语法的技术人员

2. 项目架构设计

2.1 整体架构

本应用采用鸿蒙 Next 推荐的 Stage 模型,整体架构分为三层:

┌─────────────────────────────────────────────┐
│              表现层 (UI Layer)                │
│  ┌──────────┬──────────┬──────────────────┐  │
│  │ 表单页面  │ 结果页面 │  生成按钮        │  │
│  │ FormPage │ResultPage│  GenerateBtn    │  │
│  └──────────┴──────────┴──────────────────┘  │
├─────────────────────────────────────────────┤
│              逻辑层 (Logic Layer)             │
│  ┌──────────┬──────────┬──────────────────┐  │
│  │ 状态管理  │ 模板引擎 │   表单校验       │  │
│  │ @State   │ Template │   Validation    │  │
│  └──────────┴──────────┴──────────────────┘  │
├─────────────────────────────────────────────┤
│              数据层 (Data Layer)              │
│  ┌──────────┬──────────┬──────────────────┐  │
│  │ 检讨模板  │ 作弊类型 │   字符串资源     │  │
│  │ ToneSets │ CheatType│   I18n Strings  │  │
│  └──────────┴──────────┴──────────────────┘  │
└─────────────────────────────────────────────┘

2.2 模块划分

应用功能模块清晰划分如下:

模块 实现方式 职责
表单输入 @Builder FieldRow × 4 收集考生信息
作弊选择 @Builder CheatCard × 8 多选作弊类型
语气选择 @Builder ToneButton × 3 单选检讨语气
字数选择 @Builder LengthButton × 3 单选字数档位
文本生成 generateApology() 组合模板生成
结果展示 @Builder ResultPage 显示/复制检讨书

2.3 数据流设计

应用的前后两个页面(表单页、结果页)共享同一组 @State 变量,通过单向数据流驱动 UI:

用户填写表单 → @State 变量更新 → UI 自动重渲染
        │
        ▼
点击生成 → generateApology() → 组合模板 → 写入 @State generatedText
        │
        ▼
showResult = true → 切换到结果页面 → 显示检讨书
        │
        ▼
点击"修改" → showResult = false → 回到表单页面

这个模式的关键在于:所有状态集中管理,两个页面只是同一组状态的不同呈现方式。

2.4 组件树设计

完整的 UI 组件树如下,帮助理解页面结构:

Stack (根容器)
├── Column (背景层)
└── Column (内容层)
    ├── Row (标题栏)
    │   └── Column
    │       ├── Text "考试作弊检讨书生成器"
    │       └── Text "Apology Letter Generator"
    │
    └── [条件渲染]
        ├── if (!showResult) → FormPage
        │   └── Scroll
        │       └── Column
        │           ├── SectionTitle "考生信息"
        │           ├── Column (白色卡片)
        │           │   ├── FieldRow "姓名" → TextInput
        │           │   ├── FieldRow "学号" → TextInput
        │           │   ├── FieldRow "班级" → TextInput
        │           │   └── FieldRow "考试科目" → TextInput
        │           │
        │           ├── SectionTitle "作弊类型"
        │           ├── Column (白色卡片)
        │           │   ├── Row → CheatCard × 4
        │           │   └── Row → CheatCard × 4
        │           │
        │           ├── SectionTitle "检讨语气"
        │           ├── Column (白色卡片)
        │           │   └── Row → ToneButton × 3
        │           │
        │           ├── SectionTitle "字数"
        │           ├── Column (白色卡片)
        │           │   └── Row → LengthButton × 3
        │           │
        │           ├── Button "一键生成检讨书"
        │           └── Text (错误提示)
        │
        └── if (showResult) → ResultPage
            └── Scroll
                └── Column
                    ├── Column (白色卡片)
                    │   ├── Row (标题 + 状态标签)
                    │   ├── Row (分隔线)
                    │   └── Text (检讨正文)
                    │
                    └── Row
                        ├── Button "复制"
                        ├── Button "重新生成"
                        └── Button "修改"

2.5 接口定义与类型系统

ArkTS 要求所有数据结构都必须通过接口(interface)或类(class)明确定义,不允许隐式的对象字面量:

// 作弊类型定义
interface CheatType {
  icon: string    // Emoji 图标
  label: string   // 作弊类型名称
  desc: string    // 作弊行为描述
}

// 语气模板集
interface ToneSet {
  openings: string[]    // 开头模板
  bodies: string[]      // 正文模板
  reflections: string[] // 反思模板
  promises: string[]    // 承诺模板
  closings: string[]    // 结尾模板
}

这种严格的类型定义带来了几个好处:

  1. 编译时类型检查 — 在开发阶段就能发现类型不匹配的问题
  2. IDE 智能提示 — DevEco Studio 可以根据接口定义提供自动补全
  3. 自文档化 — 接口定义本身就是最好的文档
  4. 重构安全 — 修改接口时,编译器会提示所有使用点

3. 开发环境与工程配置

3.1 环境要求

操作系统:Windows 11 22H2+
开发工具:DevEco Studio 5.0+ (Build 6.1.1)
SDK 版本:HarmonyOS Next SDK 6.1.1.100 (API 24)
Node.js:v18.x (DevEco Studio 内置)
构建工具:hvigor 6.1.1+

3.2 项目级配置 (build-profile.json5)

{
  "app": {
    "products": [
      {
        "name": "default",
        "signingConfig": "default",
        "targetSdkVersion": "6.1.1(24)",
        "compatibleSdkVersion": "6.1.1(24)",
        "runtimeOS": "HarmonyOS",
        "buildOption": {
          "strictMode": {
            "caseSensitiveCheck": true,
            "useNormalizedOHMUrl": true
          }
        }
      }
    ]
  }
}

配置要点解析:

配置项 含义
targetSdkVersion 6.1.1(24) 目标 SDK 版本为 API 24
compatibleSdkVersion 6.1.1(24) 最低兼容版本也是 API 24
runtimeOS HarmonyOS 运行时操作系统
caseSensitiveCheck true 启用文件路径大小写检查
useNormalizedOHMUrl true 使用规范化的模块引用路径

targetSdkVersioncompatibleSdkVersion 都设为 API 24 意味着我们的应用只针对鸿蒙 Next 系统运行,不需要向后兼容旧版本,这让我们可以放心使用 API 24 的所有新特性。

3.3 模块级配置 (entry/build-profile.json5)

{
  "apiType": "stageMode",
  "buildOption": {
    "resOptions": {
      "copyCodeResource": {
        "enable": false
      }
    }
  },
  "buildOptionSet": [
    {
      "name": "release",
      "arkOptions": {
        "obfuscation": {
          "ruleOptions": {
            "enable": false,
            "files": ["./obfuscation-rules.txt"]
          }
        }
      }
    }
  ]
}

apiType: "stageMode" 表明我们使用的是 Stage 模型。与旧的 FA(Feature Ability)模型相比,Stage 模型有以下优势:

对比维度 FA 模型 Stage 模型
组件管理 Ability 即页面,耦合度高 Ability 与 UI 分离,职责清晰
生命周期 绑定在 UI 上 独立于 UI,更灵活
配置方式 单文件 config.json 多文件拆分(app.json5 + module.json5)
模块化 强,支持 HAP 分包
后台能力 受限 支持长时任务

3.4 应用标识配置 (AppScope/app.json5)

{
  "app": {
    "bundleName": "com.example.demoruanjian3",
    "vendor": "example",
    "versionCode": 1000000,
    "versionName": "1.0.0",
    "icon": "$media:layered_image",
    "label": "$string:app_name"
  }
}

这里有一个容易踩坑的点:app_name 资源在 AppScope/resources/base/element/string.jsonentry/src/main/resources/base/element/string.json 中同时声明时,会产生资源冲突警告。最佳实践是由 AppScope 统一管理应用级资源,entry 中只声明模块级资源。

3.5 资源文件管理

鸿蒙的资源文件按照类型和限定词组织:

resources/
├── base/
│   ├── element/          # 基础资源(字符串、颜色、浮点数)
│   │   ├── string.json
│   │   ├── color.json
│   │   └── float.json
│   ├── media/            # 媒体资源(图片、图标)
│   │   ├── startIcon.png
│   │   ├── background.png
│   │   └── layered_image.json
│   └── profile/          # 配置文件
│       ├── main_pages.json
│       └── backup_config.json

main_pages.json 定义了应用的页面路由:

{
  "src": ["pages/Index"]
}

对于单页面应用来说,只需要这一个入口页面即可。


4. Stage 模型与 Ability 生命周期

4.1 EntryAbility 源码解析

import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';

const DOMAIN = 0x0000;

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    try {
      this.context.getApplicationContext().setColorMode(
        ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET
      );
    } catch (err) {
      hilog.error(DOMAIN, 'testTag',
        'Failed to set colorMode. Cause: %{public}s', JSON.stringify(err));
    }
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(DOMAIN, 'testTag',
          'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
        return;
      }
      hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
    });
  }
  // ... onForeground, onBackground, onDestroy
}

4.2 生命周期详解

Ability 的生命周期可表示为以下流程:

应用启动
    │
    ▼
onCreate()  ── 初始化全局配置(颜色模式等)
    │
    ▼
onWindowStageCreate()  ── 创建窗口阶段,加载页面内容
    │
    ▼
onForeground()  ── 应用进入前台,对用户可见
    │
    ├── 用户按 Home 键
    │   └── onBackground()  ── 应用进入后台
    │       └── 用户返回 → onForeground()
    │
    └── 用户退出应用
        └── onWindowStageDestroy()  ── 窗口销毁
            └── onDestroy()  ── Ability 销毁

每个阶段的本应用行为:

回调 触发时机 本应用的操作
onCreate Ability 创建 设置颜色模式,初始化日志
onWindowStageCreate 窗口创建 加载 Index 页面
onForeground 进入前台 恢复表单状态
onBackground 进入后台 保存草稿(可扩展)
onDestroy Ability 销毁 清理资源

4.3 hilog 日志系统

在 API 24 中,hilog 是推荐的日志输出方式:

const DOMAIN = 0x0000;  // 领域 ID,范围 0x0000~0xFFFF

// 不同级别的日志
hilog.debug(DOMAIN, 'TAG', 'Debug message: %{public}s', data);
hilog.info(DOMAIN, 'TAG', 'Info message: %{public}s', data);
hilog.warn(DOMAIN, 'TAG', 'Warning: %{public}s', data);
hilog.error(DOMAIN, 'TAG', 'Error: %{public}s', JSON.stringify(err));

占位符安全标记:

  • %{public}s — 公开信息,Release 版本也可见
  • %{private}s — 隐私信息,Release 版本自动脱敏
  • %d — 整数

5. ArkUI 声明式 UI 开发

5.1 从命令式到声明式

ArkUI 采用声明式 UI 开发范式,与传统命令式的核心区别在于:

命令式(传统 Android):

// 告诉系统"怎么做"
TextView tv = findViewById(R.id.text);
tv.setText("姓名");
tv.setTextColor(Color.GRAY);

声明式(ArkUI):

// 告诉系统"要什么"
Text('姓名')
  .fontSize(12)
  .fontColor('#A1887F')

声明式开发的核心优势:

  1. UI = f(state) — UI 是状态的函数,给定状态,UI 确定
  2. 自动 diff 更新 — 框架自动计算最小更新范围
  3. 可预测性 — 状态变化路径清晰,易于调试
  4. 更少代码 — 无需手动维护视图树

5.2 ArkUI 核心组件

本应用使用的 ArkUI 组件:

组件 使用位置 关键属性
Stack 根容器,背景+内容层叠 自动重叠子组件
Column 垂直布局容器 justifyContent, alignItems
Row 水平布局容器 layoutWeight, justifyContent
Scroll 滚动内容区 layoutWeight(1) 填充剩余空间
Text 显示文字 fontSize, fontColor, fontWeight
TextInput 文本输入框 placeholder, text, onChange
Button 操作按钮 backgroundColor, borderRadius
Circle 状态指示灯 fill, size
Stack 根容器 层叠背景和内容层

5.3 链式调用 API

ArkUI 的组件配置通过方法链串联,这是框架最显著的 API 风格:

Text('考试作弊检讨书生成器')
  .fontSize(18)
  .fontWeight(FontWeight.Bold)
  .fontColor('#4E342E')

链式调用的每个方法都返回组件本身,允许连续调用。这比传统的 XML 配置或 setter 方式更紧凑。

5.3.1 链式调用的工作原理

理解链式调用的内部机制有助于我们更好地使用它。每个组件方法返回的是组件实例本身(类似 Builder 设计模式),因此可以连续调用:

// Text 组件的链式调用展开理解
const text = new TextComponent('Hello')
text.setFontSize(16)      // 返回 text 实例
text.setFontColor('#FFF')  // 返回 text 实例
text.setFontWeight(Bold)   // 返回 text 实例

这种方式的核心优点是消除了重复的组件引用变量,让代码更加简洁流畅。缺点是调试时难以在中间步骤打断点,不过可以通过拆开链式调用来解决。

5.3.2 链式调用的常见模式

在本项目中,总结了几种常见的链式调用模式:

模式一:尺寸 + 颜色 + 边距(卡片类)

.width('100%')
.padding(14)
.backgroundColor(Color.White)
.borderRadius(10)
.shadow({ radius: 4, color: '#0000000D', offsetY: 2 })
.margin({ top: 10 })

模式二:字体 + 颜色 + 对齐(文字类)

.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#4E342E')
.width('100%')

模式三:尺寸 + 背景 + 圆角(按钮类)

.width(108)
.height(36)
.backgroundColor('#8D6E63')
.borderRadius(18)

将这些常用模式整理成 @Builder 方法,可以减少重复代码。

5.3.3 链式调用的顺序注意事项

链式调用的顺序通常不影响最终渲染结果,但有一些最佳实践:

  1. 容器属性优先 — 先设置 width/height 再设置 padding/margin
  2. 视觉属性集中 — 把颜色、圆角、阴影等视觉属性放在一起
  3. 事件回调放最后.onClick() 等事件绑定放在链尾
// 推荐顺序:尺寸 → 视觉 → 间距 → 事件
Button()
  .width('80%')
  .height(48)
  .backgroundColor('#8D6E63')
  .borderRadius(24)
  .margin({ top: 20 })
  .alignSelf(ItemAlign.Center)
  .onClick(() => { this.generateApology() })

5.4 TextInput 组件详解

TextInput 是本应用最核心的交互组件,用于收集考生信息:

TextInput({ placeholder: '请输入考生姓名', text: this.studentName })
  .fontSize(13)
  .fontColor('#4E342E')
  .layoutWeight(1)
  .height(36)
  .backgroundColor('#F5F0EB')
  .borderRadius(6)
  .onChange((v: string) => {
    this.studentName = v
    this.showNameTip = false
  })
  .placeholderColor('#D7CCC8')

TextInput 的构造函数接受一个对象参数,包含 placeholder(占位提示)和 text(当前值)。onChange 回调在用户输入时触发,接收当前文本作为参数。

5.5 尺寸与布局

ArkUI 提供多种尺寸设置方式:

// 固定尺寸
.width(72).height(80)

// 百分比
.width('100%')

// 权重分配(弹性布局)
.layoutWeight(1) // 填充剩余空间

对齐方式:

// 行容器:主轴水平
Row() { /* ... */ }
  .justifyContent(FlexAlign.SpaceBetween)  // 均匀分布
  .alignItems(VerticalAlign.Center)         // 垂直居中

// 列容器:主轴垂直
Column() { /* ... */ }
  .justifyContent(FlexAlign.Center)         // 垂直居中
  .alignItems(HorizontalAlign.Center)       // 水平居中

5.6 阴影与圆角

白色卡片通过阴影和圆角营造纸质感:

.backgroundColor(Color.White)
.borderRadius(10)
.shadow({ radius: 4, color: '#0000000D', offsetY: 2 })

shadow 属性接受一个对象,包含 radius(模糊半径)、color(阴影颜色)和 offsetY(垂直偏移)。


6. 状态管理与表单设计

6.1 @State 装饰器

@State 是 ArkUI 中最核心的状态管理装饰器。被 @State 修饰的变量发生变化时,所有依赖该变量的 UI 组件会自动重新渲染:

@Component
struct Index {
  @State studentName: string = ''
  @State studentId: string = ''
  @State studentClass: string = ''
  @State examSubject: string = ''
  @State cheat0: boolean = false
  @State cheat1: boolean = false
  // ... 8 个作弊类型状态
  @State selectedTone: number = 0
  @State selectedLength: number = 0
  @State generatedText: string = ''
  @State showResult: boolean = false
  @State copied: boolean = false
  @State showNameTip: boolean = false
  @State showSubjectTip: boolean = false
}

这里有一个值得注意的设计选择:为什么作弊类型不用数组 @State selectedCheatTypes: boolean[],而是拆成 8 个独立的 cheat0 ~ cheat7

这是因为 ArkTS 对 @State 数组的修改检测有限制——直接修改数组元素(arr[i] = value)不会触发 UI 更新。使用独立变量虽然代码量更多,但能确保每个状态的变更都能可靠地触发重渲染。

6.2 计算属性(getter)

ArkUI 不支持 @Computed 装饰器,但可以通过 getter 实现计算属性:

get hasCheatSelected(): boolean {
  return this.cheat0 || this.cheat1 || this.cheat2 || this.cheat3 ||
    this.cheat4 || this.cheat5 || this.cheat6 || this.cheat7
}

get selectedCheatLabels(): string {
  let result: string = ''
  for (let i = 0; i < 8; i++) {
    if (this.getCheat(i)) {
      if (result.length > 0) result += '、'
      result += this.cheatTypes[i].label
    }
  }
  return result
}

getter 在模板中作为属性使用,当依赖的 @State 变量变化时自动重新计算。

6.3 状态变更驱动 UI

以生成按钮为例,它根据当前校验状态显示不同的提示:

Button() { Text('✨ 一键生成检讨书') /* ... */ }
  .onClick(() => { this.generateApology() })

// 下方提示文字
if (this.studentName.trim().length === 0 || 
    this.examSubject.trim().length === 0 || 
    !this.hasCheatSelected) {
  Text('⚠️ 请填写姓名、考试科目并选择作弊类型')
    .fontSize(12)
    .fontColor('#BF8A7A')
}

generateApology() 校验失败时,对应的 showNameTipshowSubjectTip 被设为 true,下方的错误提示立即显示——整个过程无需手动操作 DOM。

6.4 表单校验

表单校验逻辑集中在 generateApology() 方法中:

generateApology(): void {
  // 校验:姓名为空
  if (this.studentName.trim().length === 0) {
    this.showNameTip = true
    return
  }
  // 校验:科目为空
  if (this.examSubject.trim().length === 0) {
    this.showSubjectTip = true
    return
  }
  // 校验:未选作弊类型
  if (!this.hasCheatSelected) {
    return
  }
  // 校验通过,执行生成
  // ...
}

校验失败时,对应的提示标志设为 trueFieldRow 中的错误提示文字通过 if (showTip) 条件渲染显示。

6.5 页面切换状态

表单页面和结果页面通过 @State showResult 控制切换:

build() {
  Stack() {
    // ... 标题栏
    if (!this.showResult) {
      this.FormPage()    // 表单模式
    } else {
      this.ResultPage()  // 结果模式
    }
  }
}

切换路径:

FormPage ──点击"生成"──→ showResult=true ──→ ResultPage
ResultPage ──点击"修改"──→ showResult=false ──→ FormPage
ResultPage ──点击"重新生成"──→ generateApology() ──→ ResultPage(新内容)

6.6 复制状态反馈

复制按钮被点击后显示"已复制"状态,2 秒后自动恢复:

copyText(): void {
  this.copied = true
  setTimeout(() => {
    this.copied = false
  }, 2000)
}

UI 根据 copied 状态切换显示:

Text(this.copied ? '✅ 已复制' : '📋 复制')
  .fontSize(14)
  .fontColor(Color.White)

.backgroundColor(this.copied ? '#67C23A' : '#8D6E63')

7. 模板引擎的设计与实现

7.1 模板结构设计

检讨书模板是本应用的核心逻辑。每份检讨书由五个部分组成:

┌─────────────────────────────────────┐
│  1. 开头 (openings)                  │
│     "尊敬的老师:您好!关于本次..."    │
├─────────────────────────────────────┤
│  2. 正文 (bodies)                    │
│     "经过您的批评教育,我认识到..."   │
├─────────────────────────────────────┤
│  3. 反思 (reflections)               │
│     "通过这件事,我深刻意识到..."     │
├─────────────────────────────────────┤
│  4. 承诺 (promises)                  │
│     "在此,我向您郑重承诺..."         │
├─────────────────────────────────────┤
│  5. 结尾 (closings)                  │
│     "检讨人:xxx\n2025年x月x日"       │
└─────────────────────────────────────┘

每个部分都有多个备选模板,生成时随机选择,确保每次生成的检讨书都有所不同。

7.2 三种语气风格

我们提供了三种语气风格,每种风格有独立的模板集:

🙏 诚恳认错(chengken):

“我怀着无比愧疚和懊悔的心情,向您递交这份检讨书,以深刻反省自己的错误。”

语气特征:真挚、谦卑、请求原谅。用词正式,态度诚恳,适合初犯或态度较好的学生。

📖 深刻反思(shenke):

“我的行为严重破坏了考试的公平性,对其他刻苦备考的同学造成了不公。”

语气特征:严肃、理性、上纲上线。从思想品德层面进行反思,适合情节较重或要求严格检查的情况。

😄 幽默自嘲(youmo):

“考试作弊就像吃重庆火锅时喝热汤——当时觉得爽,接下来就是火辣辣的教训。”

语气特征:轻松、自黑、有梗。用幽默的方式表达悔意,适合心态较好、与老师关系较融洽的学生。

7.3 占位符替换机制

模板中使用占位符标记需要动态替换的位置:

占位符 替换值 来源
{subject} 考试科目 this.examSubject
{cheats} 作弊类型(标签或描述) selectedCheatLabels
{name} 考生姓名 this.studentName
{date} 当前日期 todayDate

生成时通过字符串的 .replace() 方法替换:

text += this.pickRandom(tone.openings)
  .replace('{subject}', this.examSubject)
  .replace('{cheats}', this.selectedCheatLabels)

7.4 随机选择算法

pickRandom 方法从数组中随机选取一个元素:

pickRandom(arr: string[]): string {
  const idx: number = Math.floor(Math.random() * arr.length)
  return arr[idx]
}

这个方法不带泛型,参数类型明确为 string[],返回值类型明确为 string,避免了 ArkTS 对泛型类型推断的限制。

7.5 模板数据量统计

语气 开头 正文 反思 承诺 结尾 组合数
诚恳认错 2 2 2 2 2 32
深刻反思 2 2 2 2 2 32
幽默自嘲 2 2 2 2 2 32

每种语气有 32 种不同的组合方式,三种语气合计 96 种组合。这使得每次生成的检讨书都略有不同,不会出现完全相同的两篇。

7.6 模板选择逻辑

通过 getCurrentTone() 方法根据用户选择的语气返回对应的模板集:

getCurrentTone(): ToneSet {
  if (this.selectedTone === 1) return this.shenke
  if (this.selectedTone === 2) return this.youmo
  return this.chengken
}

这里没有使用对象索引访问(如 this.templates[toneKey]),因为 ArkTS 不允许通过计算属性名访问对象字段(arkts-no-props-by-index 规则)。改用显式的 if/else 分支来选择合适的模板集。


8. @Builder 组件化实践

8.1 @Builder 的基本用法

@Builder 是 ArkUI 提供的自定义构建函数装饰器,用于封装可复用的 UI 片段:

@Builder
SectionTitle(title: string) {
  Text(title)
    .fontSize(14)
    .fontWeight(FontWeight.Bold)
    .fontColor('#4E342E')
    .width('100%')
    .margin({ top: 14, bottom: 6 })
}

build() 或另一个 @Builder 中直接调用:

this.SectionTitle('👤 考生信息')
this.SectionTitle('🚫 作弊类型(可多选)')

8.2 带参数的 @Builder

本应用的所有 @Builder 都带有参数,实现不同数据、不同样式的渲染:

FieldRow — 表单输入行:

@Builder
FieldRow(label: string, value: string, onChange: (v: string) => void,
         placeholder: string, showTip: boolean) {
  Column() {
    Row() {
      Text(label)
        .fontSize(12)
        .fontColor('#A1887F')
        .width(56)
      TextInput({ placeholder: placeholder, text: value })
        // ... 样式配置
        .onChange((v: string) => { onChange(v) })
    }
    if (showTip) {
      Text('⚠️ 请填写' + label)
        .fontSize(11)
        .fontColor('#BF8A7A')
    }
  }
}

CheatCard — 作弊类型选择卡片:

@Builder
CheatCard(index: number) {
  Column() {
    Text(this.cheatTypes[index].icon).fontSize(24)
    Text(this.cheatTypes[index].label).fontSize(11)
      .fontColor(this.getCheat(index) ? '#FFFFFF' : '#5D4037')
    Text(this.cheatTypes[index].desc).fontSize(8)
      .fontColor(this.getCheat(index) ? '#FFFFFFAA' : '#A1887F')
  }
  .width(72).height(80)
  .backgroundColor(this.getCheat(index) ? '#8D6E63' : '#F5F0EB')
  .borderRadius(8)
  .onClick(() => { this.toggleCheat(index) })
}

8.3 @Builder 的限制与注意事项

在开发过程中,我们发现 ArkTS 对 @Builder 有一些严格的限制:

限制 1:不能有变量声明

// ❌ 错误:@Builder 中不能使用 let
@Builder
CheatCard(index: number) {
  let item = this.cheatTypes[index]  // 编译错误!
  let isSelected = this.getCheat(index)  // 编译错误!
  Column() { /* ... */ }
}

// ✅ 正确:直接在 UI 中使用表达式
@Builder
CheatCard(index: number) {
  Column() {
    Text(this.cheatTypes[index].icon)  // 直接访问
    Text(this.cheatTypes[index].label)
      .fontColor(this.getCheat(index) ? '#FFF' : '#5D4037')  // 直接调用
  }
}

限制 2:不能接受闭包参数作为内容区

// ❌ 错误:@Builder 不能接受 () => void 并调用它
@Builder
FormSection(title: string, content: () => void) {
  Column() {
    Text(title)
    content()  // 编译错误:不满足 UI 组件语法
  }
}

// ✅ 正确:直接内联内容,不使用 builder 包装

8.4 @Builder vs @Component

维度 @Builder @Component
复杂度 轻量 UI 片段 独立功能模块
状态管理 共享父组件 @State 可拥有自己的 @State
复用范围 当前文件内 可跨文件导入
参数传递 函数参数 @Prop / @Link
代码量 较少 较多

选择原则:当 UI 片段需要独立状态时用 @Component;仅仅是模板抽取时用 @Builder


9. ArkTS 严格模式与常见错误修复

9.1 ArkTS 语法限制概览

ArkTS 是 TypeScript 的超集,但为了编译期优化和运行性能,施加了比标准 TypeScript 更严格的语法限制。在 API 24 (SDK 6.1.1) 中,以下是本应用遇到的关键限制:

规则 ID 限制内容 本应用中的体现
arkts-no-any-unknown 禁止使用 anyunknown 类型 泛型函数需要明确类型
arkts-no-untyped-obj-literals 对象字面量必须匹配接口 模板定义需要接口声明
arkts-no-props-by-index 禁止通过索引访问对象属性 不能用 obj[key] 语法
arkts-no-inferred-generic-params 泛型函数需明确类型参数 取消泛型,使用具体类型
10905209 @Builder 中只允许 UI 语法 不能声明变量

9.2 错误修复实战

在开发过程中,我们遇到了 36 个编译错误。以下是分类修复方案:

问题 1:对象字面量类型不匹配(7 个错误)

错误信息:Object literal must correspond to some explicitly declared class or interface

原因:模板对象字面量中包含 title 字段,但 ToneSet 接口没有声明该字段。

修复:移除 title 字段,改用独立的 toneLabels 数组显示:

// 修复前
private templates: ToneTemplate = {
  chengken: { title: '诚恳认错', openings: [...], ... },
  shenke: { title: '深刻反思', openings: [...], ... },
  youmo: { title: '幽默自嘲', openings: [...], ... },
}

// 修复后
private chengken: ToneSet = { openings: [...], bodies: [...], ... }
private shenke: ToneSet = { openings: [...], bodies: [...], ... }
private youmo: ToneSet = { openings: [...], bodies: [...], ... }
private toneLabels: string[] = ['🙏 诚恳认错', '📖 深刻反思', '😄 幽默自嘲']

问题 2:索引访问被禁止(1 个错误)

错误信息:Indexed access is not supported for fields

原因:this.templates[toneKey] 使用变量作为属性名访问对象。

修复:改用 getCurrentTone() 方法通过 if/else 选择:

// 修复前
const template = this.templates[toneKey]

// 修复后
getCurrentTone(): ToneSet {
  if (this.selectedTone === 1) return this.shenke
  if (this.selectedTone === 2) return this.youmo
  return this.chengken
}

问题 3:泛型函数类型推断(5 个错误)

错误信息:Type inference in case of generic function calls is limited

原因:pickRandom<T>(arr: T[]): T 的泛型参数无法在 ArkTS 中正确推断。

修复:取消泛型,使用具体类型:

// 修复前
pickRandom<T>(arr: T[]): T { ... }

// 修复后
pickRandom(arr: string[]): string { ... }

问题 4:@Builder 中声明变量(4 个错误)

错误信息:Only UI component syntax can be written here

原因:在 @Builder 的 UI 组件开始之前使用了 let 声明变量。

修复:将变量表达式直接内联到 UI 组件中:

// 修复前
@Builder ToneButton(index: number) {
  let isSelected: boolean = this.selectedTone === index  // 错误
  Column() { /* ... */ }
}

// 修复后
@Builder ToneButton(index: number) {
  Column() {
    Text(this.toneLabels[index])
      .fontColor(this.selectedTone === index ? '#FFF' : '#5D4037')
  }
  .backgroundColor(this.selectedTone === index ? '#8D6E63' : '#F5F0EB')
}

问题 5:@Builder 中的闭包内容参数(5 个错误)

错误信息:'content()' does not meet UI component syntax

原因:在 @Builder 中接受 () => void 参数并调用它来渲染内容。

修复:取消 FormSection builder,直接在 FormPage 中内联结构:

// 修复前
this.FormSection('👤 考生信息', () => {
  this.FieldRow('姓名', ...)
  this.FieldRow('学号', ...)
})

// 修复后
this.SectionTitle('👤 考生信息')
Column() {
  this.FieldRow('姓名', ...)
  this.FieldRow('学号', ...)
}
.padding(14)
.backgroundColor(Color.White)
.borderRadius(10)
.shadow({ radius: 4, color: '#0000000D', offsetY: 2 })

9.3 修复前后对比

指标 修复前 修复后
编译错误数 36 0
代码行数 640 663
接口数量 3 2
泛型函数 1 0
@Builder 数量 9 8

9.4 ArkTS 开发建议

基于这次实战经验,给 ArkTS 开发者的建议:

  1. 避免使用泛型 — 除非必要,尽量使用具体类型
  2. 避免索引访问 — 用 if/else 或 switch 替代 obj[key]
  3. 提前声明接口 — 所有对象字面量都需要匹配接口
  4. @Builder 保持简单 — 不要在 @Builder 中声明变量
  5. 使用 getter — 替代计算属性
  6. 别用 any/unknown — 所有类型必须明确

10. UI/UX 设计:纸笔风格视觉系统

10.1 色彩体系

本应用采用温暖、亲切的纸笔风格(Paper & Pen)色彩系统,与传统冷色调的科技应用形成对比:

层级 色值 用途
背景 #F5F0EB 米白色仿纸背景
卡片 #FFFFFF 白色卡片,带轻微阴影
主文字 #4E342E 深棕色标题
正文 #5D4037 正文文字
辅助 #A1887F 副标题、提示文字
主色调 #8D6E63 棕色按钮、选中态
浅色 #D7CCC8 边框、分隔线
浅背景 #EFEBE9 标签背景
警告 #BF8A7A 错误提示文字

色彩设计理念:

  • 米色背景模仿纸张质感,降低视觉疲劳
  • 棕色主色调传达温暖、真诚的感觉
  • 对比度适中,文字与背景的对比度在 4.5:1 以上,符合无障碍标准

10.2 排版系统

主标题:18sp, Bold, #4E342E
标题:14sp, Bold, #4E342E
标签:12sp, Regular, #A1887F
输入框:13sp, Regular, #4E342E
按钮文字:16sp, Bold, #FFFFFF
辅助文字:9-11sp, Regular, #A1887F

10.3 间距与圆角

// 间距系统
.margin({ top: 14, bottom: 6 })  // 标题下边距
.padding(14)                       // 卡片内边距
.margin({ top: 10 })               // 卡片间距

// 圆角系统
.borderRadius(10)  // 大卡片
.borderRadius(8)   // 小卡片
.borderRadius(24)  // 生成按钮(全圆角)
.borderRadius(18)  // 选择按钮
.borderRadius(6)   // 输入框

10.4 选择卡片交互设计

作弊类型卡片选中前后的视觉差异:

状态 背景色 文字色 边框
未选中 #F5F0EB #5D4037 1px #D7CCC8
已选中 #8D6E63 #FFFFFF 无边框
.backgroundColor(this.getCheat(index) ? '#8D6E63' : '#F5F0EB')
.borderWidth(this.getCheat(index) ? 0 : 1)
.borderColor('#D7CCC8')

10.5 结果页面设计

结果页面模拟了纸质检讨书的视觉效果:

Column() {
  // 头部:标题 + 语气标签
  Row() {
    Text('📄 检讨书').fontSize(16).fontWeight(FontWeight.Bold)
    Row() {
      Circle().size({ width: 6, height: 6 }).fill('#8D6E63')
      Text('已生成').fontSize(11).fontColor('#8D6E63')
    }
    .backgroundColor('#EFEBE9')
    .borderRadius(10)
  }

  // 分隔线
  Row().width('100%').height(1).backgroundColor('#D7CCC8')

  // 检讨正文
  Text(this.generatedText)
    .fontSize(13)
    .fontColor('#5D4037')
    .lineHeight(22)
}
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({ radius: 8, color: '#0000001A', offsetY: 4 })

11. 条件渲染与页面切换

11.1 if/else 条件渲染

ArkUI 使用标准的 if/else 语句实现条件渲染:

if (!this.showResult) {
  this.FormPage()
} else {
  this.ResultPage()
}

showResult 变化时,ArkUI 自动销毁旧页面、创建新页面。这确保了 UI 始终与状态同步。

11.2 表单页面的结构

FormPage 使用 Scroll + Column 实现可滚动的表单:

@Builder
FormPage() {
  Scroll() {
    Column() {
      // 四个表单区域按顺序排列
      this.SectionTitle('👤 考生信息')
      Column() { /* 四个输入框 */ }
        .padding(14).backgroundColor(Color.White).borderRadius(10)
        .shadow({ radius: 4, color: '#0000000D', offsetY: 2 })

      this.SectionTitle('🚫 作弊类型(可多选)')
      // ...

      this.SectionTitle('🎭 检讨语气')
      // ...

      this.SectionTitle('📏 字数')
      // ...

      Button('✨ 一键生成检讨书') // ...
    }
    .width('100%')
    .padding({ left: 16, right: 16 })
  }
  .layoutWeight(1)
  .width('100%')
}

11.3 结果页面的结构

ResultPage 展示生成的检讨书和三个操作按钮:

@Builder
ResultPage() {
  Scroll() {
    Column() {
      // 检讨书卡片
      Column() {
        Row() { /* 标题 + 状态标签 */ }
        Row() { /* 分隔线 */ }
        Text(this.generatedText) /* 正文 */
      }
      .padding(16).backgroundColor(Color.White).borderRadius(12)

      // 操作按钮
      Row() {
        Button('📋 复制')
        Button('🔄 重新生成')
        Button('✏️ 修改')
      }
    }
  }
  .layoutWeight(1)
  .width('100%')
}

11.4 错误提示的条件显示

错误提示在 FormPage 底部显示,当校验不通过时可见:

if (this.studentName.trim().length === 0 ||
    this.examSubject.trim().length === 0 ||
    !this.hasCheatSelected) {
  Text('⚠️ 请填写姓名、考试科目并选择作弊类型')
    .fontSize(12)
    .fontColor('#BF8A7A')
    .alignSelf(ItemAlign.Center)
}

同时,在每个 FieldRow 中,单独的字段错误提示也使用条件渲染:

if (showTip) {
  Text('⚠️ 请填写' + label)
    .fontSize(11)
    .fontColor('#BF8A7A')
}

12. 鸿蒙 API 24 特性应用

12.1 API 24 相比前代的重要更新

特性 说明 对本项目的意义
增强的 Stage 模型 更好的生命周期管理 页面状态更可控
ArkUI 3.0 更流畅的动画和渲染 UI 响应更迅速
严格模式编译 编译时类型检查 提前发现潜在错误
包管理优化 支持 OHM 规范 模块引用更规范

12.2 实际使用到的 API 24 特性

1. Stage 模型 API(@kit.AbilityKit)

import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
  • UIAbility — API 24 中 Ability 的基类
  • ConfigurationConstant.ColorMode — 颜色模式配置

2. ArkUI 组件库(@kit.ArkUI)

import { window } from '@kit.ArkUI';
  • window.WindowStage.loadContent() — 加载页面
  • ArkUI 内置组件:Stack, Column, Row, Scroll, Text, TextInput, Button, Circle

3. 日志 API(@kit.PerformanceAnalysisKit)

import { hilog } from '@kit.PerformanceAnalysisKit';
  • 结构化日志,支持隐私标记(%{public}s / %{private}s

12.3 ArkTS 编译配置

build-profile.json5 中,strictMode 配置项启用了更严格的编译检查:

"buildOption": {
  "strictMode": {
    "caseSensitiveCheck": true,
    "useNormalizedOHMUrl": true
  }
}
  • caseSensitiveCheck:确保文件引用路径与实际文件名大小写一致。例如 pages/Indexpages/index 是不同的。
  • useNormalizedOHMUrl:规范模块引用路径,避免路径解析歧义。

12.4 hvigor 构建工具

hvigor 是鸿蒙项目的构建工具,类似于 Android 的 Gradle 或前端的 Webpack。在本次开发中,我们使用了以下命令:

# 构建 HAP 包
hvigorw assembleHap --mode module -p module=entry@default

# 查看可用任务
hvigorw tasks

# 清理缓存
hvigorw prune

hvigor 的构建流程包括以下步骤:

PreBuild → CreateModuleInfo → ProcessProfile → CompileResource
→ CompileArkTS → GeneratePkgModuleJson → PackageHap → SignHap

其中 CompileArkTS 是核心步骤,负责将 ArkTS 源码编译为字节码。这也是出现编译错误的阶段——好消息是 ArkTS 编译器提供了详细的错误信息,包括行号、错误代码和规则 ID。

12.5 @kit 模块化导入

在 API 24 中,鸿蒙推荐使用 @kit.xxx 的方式进行模块化导入,取代了旧的 @ohos.xxx 方式:

// API 24 推荐方式
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';

// 旧方式(仍然兼容但不推荐)
// import ability from '@ohos.ability.ability';
// import hilog from '@ohos.hilog';

@kit 模块是 API 24 新增的特性,它将相关的 API 打包成更高层次的模块,让导入更加直观。例如 @kit.AbilityKit 包含了所有与 Ability 相关的 API。

12.6 ArkTS 与标准 TypeScript 的差异

对于有 TypeScript 经验的开发者,以下差异需要注意:

特性 TypeScript ArkTS
any 类型 可用 禁止
unknown 类型 可用 禁止
索引签名 [key: string] 可用 受限制
泛型函数 完全支持 有限支持
联合类型 完全支持 有限支持
装饰器 实验性 原生支持(@State/@Builder等)
对象字面量 可隐式推断 必须匹配接口
null/undefined 独立类型 统一使用可选链

这些限制的存在是为了让 ArkTS 编译器可以进行更深入的静态分析,从而生成更高效的字节码。

12.4 未来的扩展:真实 API

如果需要将本应用扩展为生产级应用,可以考虑接入以下鸿蒙 API:

API 包路径 用途
剪贴板 @ohos.pasteboard 真正的复制到剪贴板功能
文件存储 @ohos.file.fs 保存检讨书到本地
分享 @ohos.share 分享检讨书到其他应用
打印 @ohos.print 打印检讨书
推送 @ohos.commonEventManager 通知相关功能

13. 安全与实践考虑

13.1 数据隐私

检讨书可能包含个人身份信息(姓名、学号、班级)。虽然本应用是纯本地应用,不涉及网络传输,但在正式发布时需要注意:

  1. 数据存储安全:如果增加保存功能,应使用 @ohos.data.preferences 加密存储
  2. 隐私声明:明确告知用户数据仅在本地处理

13.2 输入验证

// 防止空名称
if (this.studentName.trim().length === 0) {
  this.showNameTip = true
  return
}

13.3 防滥用

本应用仅供教育用途,建议在正式使用时添加免责声明:

@Builder
Disclaimer() {
  AlertDialog.show({
    title: '免责声明',
    message: '本工具仅供学习参考,请勿用于实际作弊行为。' +
             '诚信考试,从我做起。',
    confirm: { value: '我知道了', action: () => {} }
  })
}

14. 踩坑与经验总结

14.1 @Builder 中的变量声明

问题:在 @Builder 方法的 UI 组件开始之前使用 let 声明变量会导致编译错误。

错误信息Only UI component syntax can be written here

解决方案:将变量表达式直接内联到 UI 属性中,或者将计算逻辑移到 @Builder 外部的普通方法中。

14.2 对象字面量必须匹配接口

问题:ArkTS 不允许声明未匹配接口的对象字面量。所有对象字面量必须有明确的类型注解或匹配已声明的接口。

解决方案:提前创建接口定义,所有对象字面量使用接口类型注解。

14.3 避免使用索引访问

问题:ArkTS 不允许使用变量通过索引访问对象属性(obj[key])。

错误信息Indexed access is not supported for fields

解决方案:使用 if/else 链或 switch 语句替代。

14.4 资源冲突

问题:在 AppScopeentry 模块的 string.json 中同时声明 app_name 会导致资源冲突警告。

错误信息'app_name' conflict, first declared at AppScope... but declared again

解决方案:应用级资源(如 app_name)只在 AppScope 中声明,entry 中只声明模块级资源。

14.5 数组状态管理

问题@State 数组的直接索引修改(arr[i] = value)不会触发 UI 更新。

解决方案:拆分为独立的 @State 变量,或使用 [...newArray] 创建新数组替换旧数组。

// 方案 A:独立变量
@State cheat0: boolean = false
@State cheat1: boolean = false
// ... 8 个变量

// 方案 B:创建新数组
@State cheats: boolean[] = new Array(8).fill(false)
this.cheats = [...this.cheats.slice(0, i), true, ...this.cheats.slice(i + 1)]

方案 A 代码更明确,方案 B 更简洁。根据项目规模选择。

14.6 字符串换行

在 ArkTS 中,字符串字面量过长时需要合理换行。ArkTS 支持字符串连接和模板字符串:

// 字符串连接
'尊敬的老师:\n\n您好!关于本次' + subject + '考试中的违纪行为,'

// 直接包含换行符
'第一行\n第二行\n第三行'

对于多行模板,每行字符串用 + 连接,保持代码整洁。注意 \n 是换行符,在检讨书生成中频繁使用。

14.7 开发效率建议

1. 渐进式开发

先写核心逻辑(模板引擎),再写 UI 层。这样可以在 DevEco Studio 的 Previewer 中快速验证功能。

2. 利用 Previewer

ArkUI 的 Previewer 工具可以实时预览 UI 效果。对于 @Builder 中的 UI 修改,保存后即可看到效果,无需完整构建。

3. 构建日志定位

出错了先看错误行号和错误类型。ArkTS 的错误信息通常包含规则 ID(如 arkts-no-props-by-index),搜索这个 ID 可以找到详细的规则说明。

4. 善用类型注解

ArkTS 的类型推断有限,显式添加类型注解可以减少编译错误:

const now: Date = new Date()       // ✅ 显式类型
const idx: number = Math.floor(...) // ✅ 显式类型

14.8 常见编译错误速查表

错误信息 原因 解决方案
Object literal must correspond to some explicitly declared class or interface 对象字面量没有匹配的接口 创建接口或添加类型注解
Indexed access is not supported for fields 使用变量索引访问对象 使用 if/else 替代
Only UI component syntax can be written here @Builder 中有非 UI 语句 将逻辑移到 builder 外部
Type inference in case of generic function calls is limited 泛型类型无法推断 取消泛型,使用具体类型
'xxx' conflict, first declared at ... but declared again 资源重复声明 统一资源声明位置
Cannot find name 'xxx' 变量未定义或拼写错误 检查变量名和 import
Object is of type 'unknown' 索引访问返回 unknown 类型 改用类型明确的方法
'content()' does not meet UI component syntax @Builder 中调用了函数 内联 UI 内容

14.9 从 36 个编译错误到 0 的思考

回顾这次修复 36 个编译错误的过程,有几点值得分享的思考:

第一,错误数量不等于项目复杂度。 36 个错误听起来很多,但归类后只有 6 个根本原因。一旦理解了 ArkTS 的核心限制,大部分错误可以通过全局替换一次性修复。

第二,编译错误是最好的老师。 ArkTS 编译器提供的错误信息非常详细,包括错误 ID、行号和具体原因。认真阅读每条错误信息,理解背后的规则,是掌握 ArkTS 的最快途径。

第三,严格模式是好事。 虽然 ArkTS 的严格模式给开发初期带来了更多编译错误,但它确保了我们写出更健壮的代码。这些错误如果在运行时才发现,调试成本会高得多。

第四,渐进式修复策略。 面对大量错误时,不要试图一次性全部修复。错误之间存在依赖关系——前几个错误的修复可能会消除后续的错误。先修复根源性错误(如接口定义、类型声明),再修复派生错误。

14.10 实际使用案例

为了帮助你更好地理解这个应用的使用场景,这里提供三个实际生成的检讨书示例:

示例一:诚恳认错版

尊敬的老师:

您好!关于本次高等数学考试中的违纪行为,我怀着无比愧疚和懊悔的心情,向您递交这份检讨书,以深刻反省自己的错误。

经过您的批评教育和自己的深刻反思,我认识到考试作弊是一种极其错误的行为。携带小抄不仅违反了校规校纪,更是对自己学习成果的不负责任。学习是一个循序渐进的过程,考试的目的在于检验我们真实的学习水平,而我却用欺骗的方式来对待它,实在是愧对老师的教诲,也辜负了父母的期望。

通过这件事,我深刻意识到诚信考试的重要性。分数固然重要,但比分数更重要的是做人的品格。一次作弊行为可能会让我暂时获得"好成绩",但失去的是老师对我的信任,以及自己内心的安宁。我决心以此为戒,在今后的学习和生活中,时刻铭记诚信二字的分量。

在此,我向您郑重承诺:第一,今后决不再犯类似的错误,严格遵守考试纪律;第二,我会利用课余时间补上高等数学的知识漏洞,通过自己的努力取得真实的进步;第三,我会主动向同学们宣传诚信考试的重要性,以自己的教训警示他人。恳请老师给我一次改过自新的机会。

检讨人:张明轩
2025年6月15日

示例二:幽默自嘲版

尊敬(且此刻一定对我很失望)的老师:

您好!关于大学英语考试,我有一份"精彩"的检讨要呈上。说真的,使用手机这个操作,现在回想起来我自己都替自己尴尬。

经过(被抓住后的)冷静思考,我认识到考试作弊这件事,大概是世界上最不划算的买卖了。冒着被发现的巨大风险,承受着内心的煎熬,最后换来的可能还不如自己老老实实写对的几分。使用手机这个计划,从我决定实施的那一刻起就注定是个悲剧——事实证明,我在"做坏事"这方面的天赋值为零。

这件事给我上了生动的一课:人生没有捷径,如果有,那一定是通向"检讨书"的捷径。考试作弊就像吃重庆火锅时喝热汤——当时觉得爽,接下来就是火辣辣的教训。我保证,这是我人生中第一份也是最后一份检讨书,因为这种"体验"一次就够了。

在此我立下FLAG:第一,以后考试绝对"目不斜视",专注于自己的试卷;第二,我会把用在"研究作弊方法"上的精力用来研究大学英语,争取下次靠实力及格;第三,如果下次考试进步了,希望老师能忘记这次的不愉快,记住我改过自新的帅气背影。

一个正在反省的学生:李小明
2025年6月15日

P.S. 这份检讨写得很辛苦,下次不想再写了。

示例三:深刻反思版

尊敬的老师、校领导:

我以最沉痛的心情,就本人在线性代数考试中交头接耳、偷看他人的严重违纪行为,向学校和老师作出深刻检讨。

我的行为严重破坏了考试的公平性,对其他刻苦备考的同学造成了不公。考试不仅是对知识的检验,更是对人格的考验。交头接耳、偷看他人的行为,反映出我在思想品德上的严重缺失。我辜负了学校的培养,辜负了老师的信任,也辜负了父母的期望。作为一名学生,我没有尽到最基本的本分。

这次违纪事件对我是一次刻骨铭心的教训。它让我深刻认识到:第一,诚信是立身之本,任何时候都不能放弃;第二,学习没有捷径,必须付出真实的努力;第三,规则意识是公民基本素养,无视规则终将付出代价。我将把这次教训铭记在心,作为人生的重要警示。

为弥补过错,我郑重承诺:一、深刻反省,写出书面检讨并在班级公开宣读;二、自觉接受学校给予的纪律处分,绝无怨言;三、以此为戒,在剩余的学习生涯中诚信考试、踏实学习;四、主动参与诚信考试宣传活动,用自己的教训警示他人。请学校和老师监督我。

检讨人:王强
2025年6月15日

附:本人愿意接受学校相应纪律处分。

这些示例展示了三种语气风格的实际效果。用户可以根据自己的情况和性格选择合适的语气风格,生成个性化的检讨书。


15. 结语与展望

15.1 项目回顾

本文从零构建了一个鸿蒙 Next 考试作弊检讨书生成器,涵盖了:

  • 项目架构:Stage 模型、三层架构、组件树设计
  • UI 开发:ArkUI 声明式 UI、@Builder 组件化、纸笔风格设计
  • 状态管理:@State 驱动、计算属性、表单校验
  • 模板引擎:多语气模板、占位符替换、随机组合
  • ArkTS 严格模式:语法限制、错误修复、开发建议
  • API 24 配置:stageMode、编译选项、资源管理

15.2 扩展方向

这个应用可以进一步扩展为:

  1. 更多语气风格 — 增加"家庭温情版"“严格版”"简约版"等更多风格
  2. 自定义模板 — 允许用户编辑和保存自定义模板
  3. 导出功能 — 支持 PDF/Word 格式导出
  4. 云端同步 — 跨设备同步模板和草稿
  5. AI 辅助生成 — 接入端侧 AI 模型,根据用户输入的描述生成个性化检讨书
  6. 多语言支持 — 支持英文、日文等多语言检讨书

15.3 写在最后

鸿蒙 Next 作为一个全新的操作系统生态,ArkTS 的严格模式虽然给初学开发者带来了一定的学习曲线,但它强制开发者写出更规范、更安全的代码。通过本次实战,我们深入理解了 ArkTS 的类型系统、编译期约束和声明式 UI 开发范式。

检讨书生成器虽是一个看似简单的工具型应用,但它涵盖了表单处理、模板引擎、条件渲染、状态管理等多个核心开发场景,可以作为鸿蒙入门开发者的一个完整的练手项目。

最后,技术本身是中性的。检讨书生成器可以帮助学生快速完成检讨流程,但真正重要的还是认识到诚信考试的意义——工具可以被使用,但成长没有捷径


附录

A. 项目完整源码

DemoRuanjian3/
├── build-profile.json5                    # 项目级配置 (API 24)
├── AppScope/
│   ├── app.json5                          # 应用级配置
│   └── resources/base/element/string.json # 应用名资源
└── entry/
    ├── build-profile.json5                # 模块级配置 (Stage 模型)
    ├── oh-package.json5                   # 依赖管理
    └── src/main/ets/
        ├── entryability/
        │   └── EntryAbility.ets           # Ability 生命周期
        └── pages/
            └── Index.ets                  # 主页面 (663行)

B. 关键词索引

关键词 章节
@State 6.1
@Builder 8.1
Stage 模型 4.1
API 24 3.2, 12.1
ArkUI 5.1
ArkTS 严格模式 9.1
模板引擎 7.1
TextInput 5.4
条件渲染 11.1
编译错误修复 9.2

C. 参考资料

  1. HarmonyOS Next 开发者文档
  2. ArkUI 组件参考
  3. ArkTS 语言限制说明
  4. Stage 模型开发指南
  5. hvigor 构建工具配置
Logo

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

更多推荐