【鸿蒙原生应用开发--ArkUI--003】TodoApp - 待办事项应用教程

TodoApp - 待办事项应用教程

项目介绍

项目背景

待办事项(Todo List)是每个开发者入门时必做的经典项目之一。它涵盖了现代应用开发中的许多核心概念:数据管理、用户交互、列表渲染、状态管理等。通过构建一个功能完善的待办事项应用,你将深入理解 HarmonyOS NEXT 中的数据驱动 UI 开发模式。

应用场景

待办事项应用在日常生活中有着广泛的应用场景:

  • 个人任务管理:记录日常待办事项,如购物清单、学习计划等
  • 项目管理:跟踪项目进度,分配任务
  • 工作安排:管理工作任务,设置优先级
  • 习惯养成:记录每日习惯,如运动、阅读等

功能特性

本待办事项应用实现了以下功能:

  1. 添加任务:用户可以输入新的待办事项
  2. 完成标记:点击复选框标记任务完成状态
  3. 删除任务:删除不需要的任务
  4. 筛选查看:按状态筛选(全部/进行中/已完成)
  5. 统计信息:显示任务总数和完成情况
  6. 清除功能:一键清除所有已完成任务

最终效果

应用界面分为以下区域:

  • 头部区域:显示标题和统计信息
  • 输入区域:输入框和添加按钮
  • 筛选标签:切换查看不同状态的任务
  • 任务列表:显示任务列表,支持滑动
  • 底部统计:显示任务统计信息

技术栈

  • 开发框架:HarmonyOS NEXT API 23
  • 编程语言:ArkTS
  • UI 框架:ArkUI 声明式 UI
  • 数据管理:@State 状态管理
  • 列表渲染:ForEach 循环

开发环境准备

1. 创建项目

创建一个新的 HarmonyOS NEXT 项目:

方式一:使用 DevEco Studio 创建

  1. 打开 DevEco Studio
  2. 选择 "Create HarmonyOS Project"
  3. 选择 "Empty Ability" 模板
  4. 设置项目名称为 "TodoApp"
  5. 选择 API 版本为 23

方式二:复制模板项目

  1. 复制 project-template 目录
  2. 重命名为 "TodoApp"
  3. 修改配置文件

2. 项目结构

复制代码
TodoApp/
├── AppScope/                    # 应用全局配置
│   ├── app.json5               # 应用级配置
│   └── resources/              # 应用级资源
├── entry/                       # 主模块
│   └── src/main/
│       ├── ets/                # ArkTS 源代码
│       │   ├── entryability/   # UIAbility 入口
│       │   └── pages/          # 页面组件
│       └── resources/          # 资源文件
├── build-profile.json5         # 构建配置
└── oh-package.json5            # 依赖配置

知识点讲解

1. 接口定义 - interface 详解

在 ArkTS 中,使用 interface 定义数据结构的类型。接口是 TypeScript 中最重要的概念之一,它用于定义对象的形状。

基本语法:

typescript 复制代码
interface 接口名 {
  属性名: 类型;
  属性名: 类型;
  // ...
}

待办事项接口定义:

typescript 复制代码
// 定义待办事项的数据结构
interface TodoItem {
  id: number;        // 唯一标识,用于区分不同的任务
  text: string;      // 任务内容,用户输入的任务描述
  completed: boolean; // 是否完成,标记任务的完成状态
  createdAt: string;  // 创建时间,记录任务创建的时间
}

接口的使用:

typescript 复制代码
// 创建符合接口的对象
const todo: TodoItem = {
  id: 1,
  text: '学习 HarmonyOS',
  completed: false,
  createdAt: '2024-01-01'
};

// 接口数组
const todos: TodoItem[] = [
  { id: 1, text: '任务1', completed: false, createdAt: '2024-01-01' },
  { id: 2, text: '任务2', completed: true, createdAt: '2024-01-02' }
];

可选属性:

typescript 复制代码
interface TodoItem {
  id: number;
  text: string;
  completed: boolean;
  createdAt: string;
  priority?: number;  // 可选属性,使用 ? 标记
  tags?: string[];    // 可选属性
}

2. @State 数组状态详解

@State 可以管理数组类型的状态,当数组内容变化时 UI 自动更新。

数组状态定义:

typescript 复制代码
@State todos: TodoItem[] = [];      // 待办事项数组
@State newTodoText: string = '';    // 新任务输入文本
@State nextId: number = 1;          // 下一个任务ID
@State filter: number = 0;          // 筛选条件:0-全部,1-进行中,2-已完成

数组操作方法:

typescript 复制代码
// 1. 添加元素 - push()
this.todos.push({
  id: this.nextId++,
  text: '新任务',
  completed: false,
  createdAt: new Date().toLocaleDateString()
});

// 2. 删除元素 - filter()
this.todos = this.todos.filter(todo => todo.id !== targetId);

// 3. 修改元素 - 通过索引
const index = this.todos.findIndex(todo => todo.id === targetId);
if (index >= 0) {
  this.todos[index].completed = true;
}

// 4. 查找元素 - find()
const todo = this.todos.find(todo => todo.id === targetId);

// 5. 查找索引 - findIndex()
const index = this.todos.findIndex(todo => todo.id === targetId);

// 6. 遍历数组 - forEach()
this.todos.forEach(todo => {
  console.info(todo.text);
});

// 7. 映射数组 - map()
const texts = this.todos.map(todo => todo.text);

// 8. 过滤数组 - filter()
const completedTodos = this.todos.filter(todo => todo.completed);

// 9. 排序数组 - sort()
this.todos.sort((a, b) => a.id - b.id);

3. TextInput 组件 - 文本输入详解

TextInput 组件用于创建文本输入框,支持占位符、输入事件等。

基本用法:

typescript 复制代码
TextInput({ placeholder: '请输入内容', text: this.inputText })
  .width('100%')
  .height(48)
  .fontSize(16)
  .backgroundColor('#FFFFFF')
  .borderRadius(8)
  .onChange((value: string) => {
    this.inputText = value;
  })

核心属性:

typescript 复制代码
TextInput({ 
  placeholder: '占位符文本',  // 输入框为空时显示的提示文本
  text: this.inputText        // 绑定的文本变量
})
  .width('100%')              // 宽度
  .height(48)                 // 高度
  .fontSize(16)               // 字体大小
  .fontColor('#333333')       // 字体颜色
  .placeholderColor('#999999') // 占位符颜色
  .backgroundColor('#FFFFFF') // 背景颜色
  .borderRadius(8)            // 圆角
  .padding({ left: 12, right: 12 })  // 内边距
  .type(InputType.Normal)     // 输入类型
  .onChange((value: string) => {
    // 输入内容变化时触发
    this.inputText = value;
  })
  .onSubmit(() => {
    // 按下回车键时触发
    this.handleSubmit();
  })

输入类型:

typescript 复制代码
// 普通文本
.type(InputType.Normal)

// 密码输入
.type(InputType.Password)

// 数字输入
.type(InputType.Number)

// 电话号码
.type(InputType.PhoneNumber)

// 邮箱
.type(InputType.Email)

事件处理:

typescript 复制代码
TextInput({ text: this.inputText })
  .onChange((value: string) => {
    // 每次输入内容变化时触发
    console.info('输入内容:', value);
  })
  .onSubmit(() => {
    // 按下回车键时触发
    console.info('提交');
  })
  .onFocus(() => {
    // 获得焦点时触发
    console.info('获得焦点');
  })
  .onBlur(() => {
    // 失去焦点时触发
    console.info('失去焦点');
  })

4. Checkbox 组件 - 复选框详解

Checkbox 组件用于创建复选框,支持选中状态和状态变化事件。

基本用法:

typescript 复制代码
Checkbox()
  .select(this.isChecked)
  .selectedColor('#6366F1')
  .onChange((value: boolean) => {
    this.isChecked = value;
  })

核心属性:

typescript 复制代码
Checkbox()
  .select(this.isChecked)        // 选中状态
  .selectedColor('#6366F1')      // 选中时的颜色
  .unselectedColor('#D1D5DB')    // 未选中时的颜色
  .width(24)                     // 宽度
  .height(24)                    // 高度
  .onChange((value: boolean) => {
    // 状态变化时触发
    this.isChecked = value;
  })

在列表中的使用:

typescript 复制代码
ForEach(this.todos, (todo: TodoItem) => {
  Row() {
    Checkbox()
      .select(todo.completed)
      .selectedColor('#6366F1')
      .onChange((value: boolean) => {
        // 更新任务完成状态
        const index = this.todos.findIndex(t => t.id === todo.id);
        if (index >= 0) {
          this.todos[index].completed = value;
        }
      })
    
    Text(todo.text)
      .fontSize(16)
      .margin({ left: 12 })
  }
})

5. List 组件 - 列表详解

List 组件用于创建可滚动的列表,适合展示大量数据。

基本用法:

typescript 复制代码
List() {
  ListItem() {
    Text('列表项1')
  }
  ListItem() {
    Text('列表项2')
  }
}
.width('100%')
.layoutWeight(1)

核心属性:

typescript 复制代码
List()
  .width('100%')              // 宽度
  .layoutWeight(1)            // 占据剩余空间
  .divider({                  // 分割线
    strokeWidth: 1,
    color: '#E5E7EB'
  })
  .edgeEffect(EdgeEffect.Spring)  // 边缘效果
  .scrollBar(BarState.On)     // 滚动条显示
  .cachedCount(10)            // 缓存数量

边缘效果:

typescript 复制代码
// 弹性效果
.edgeEffect(EdgeEffect.Spring)

// 阴影效果
.edgeEffect(EdgeEffect.Fade)

// 无效果
.edgeEffect(EdgeEffect.None)

滚动条显示:

typescript 复制代码
// 自动显示
.scrollBar(BarState.Auto)

// 始终显示
.scrollBar(BarState.On)

// 始终隐藏
.scrollBar(BarState.Off)

6. ListItem 组件 - 列表项详解

ListItemList 的子组件,代表列表中的一项。

基本用法:

typescript 复制代码
ListItem() {
  Row() {
    Text('列表项内容')
  }
  .width('100%')
  .padding(16)
  .backgroundColor('#FFFFFF')
}
.margin({ bottom: 8 })

点击事件:

typescript 复制代码
ListItem() {
  Text('可点击的列表项')
}
.onClick(() => {
  console.info('列表项被点击');
})

滑动删除:

typescript 复制代码
ListItem() {
  Text('可滑动删除')
}
.swipeAction({
  end: this.SwipeDeleteButton()
})

@Builder SwipeDeleteButton() {
  Button('删除')
    .width(80)
    .height('100%')
    .backgroundColor('#EF4444')
    .onClick(() => {
      // 删除操作
    })
}

7. ForEach 循环渲染详解

ForEach 用于遍历数组并渲染列表项。

基本语法:

typescript 复制代码
ForEach(
  array,           // 数据数组
  itemGenerator,   // 渲染函数
  keyGenerator     // 键生成函数(可选,用于性能优化)
)

完整示例:

typescript 复制代码
ForEach(
  this.todos,  // 数据数组
  (todo: TodoItem, index: number) => {  // 渲染函数
    ListItem() {
      Row() {
        Checkbox()
          .select(todo.completed)
        
        Text(todo.text)
          .fontSize(16)
      }
    }
    .margin({ bottom: 8 })
  },
  (todo: TodoItem) => todo.id.toString()  // 键生成函数
)

键生成函数的作用:

键生成函数用于为每个列表项生成唯一的标识符,帮助框架识别哪些项发生了变化,从而优化渲染性能。

typescript 复制代码
// 使用唯一ID作为键
(todo: TodoItem) => todo.id.toString()

// 使用索引作为键(不推荐,性能较差)
(item: TodoItem, index: number) => index.toString()

8. TextDecorationType - 文本装饰详解

用于设置文本的装饰效果,如下划线、删除线等。

装饰类型:

typescript 复制代码
// 无装饰
.decoration({ type: TextDecorationType.None })

// 下划线
.decoration({ type: TextDecorationType.Underline })

// 删除线
.decoration({ type: TextDecorationType.LineThrough })

// 上划线
.decoration({ type: TextDecorationType.Overline })

装饰样式:

typescript 复制代码
// 实线
.decoration({ 
  type: TextDecorationType.Underline,
  style: TextDecorationStyle.Solid 
})

// 虚线
.decoration({ 
  type: TextDecorationType.Underline,
  style: TextDecorationStyle.Dashed 
})

// 点线
.decoration({ 
  type: TextDecorationType.Underline,
  style: TextDecorationStyle.Dotted 
})

在待办事项中的应用:

typescript 复制代码
Text(todo.text)
  .fontSize(16)
  .fontColor(todo.completed ? '#9CA3AF' : '#111827')
  .decoration({
    type: todo.completed ? TextDecorationType.LineThrough : TextDecorationType.None
  })

9. 条件渲染 - if 语句详解

在 ArkUI 中可以使用 if 语句进行条件渲染。

基本语法:

typescript 复制代码
Column() {
  if (condition) {
    // 条件为真时显示
    Text('条件为真')
  } else {
    // 条件为假时显示
    Text('条件为假')
  }
}

复杂条件:

typescript 复制代码
Column() {
  if (this.todos.length === 0) {
    // 没有任务时显示
    Text('暂无任务')
  } else if (this.todos.length < 5) {
    // 任务少于5个时显示
    List() { ... }
  } else {
    // 任务较多时显示
    List() { ... }
  }
}

条件渲染与数组过滤:

typescript 复制代码
Column() {
  if (this.getFilteredTodos().length === 0) {
    // 没有符合条件的任务
    Text('没有任务')
  } else {
    // 显示任务列表
    List() {
      ForEach(this.getFilteredTodos(), (todo: TodoItem) => {
        ListItem() { ... }
      })
    }
  }
}

10. Blank 组件 - 空间填充详解

Blank 组件用于在 RowColumn 中填充剩余空间。

基本用法:

typescript 复制代码
Row() {
  Text('左侧内容')
  Blank()  // 填充中间空间
  Text('右侧内容')
}
.width('100%')

多空白区域:

typescript 复制代码
Row() {
  Text('左')
  Blank()  // 第一个空白
  Text('中')
  Blank()  // 第二个空白
  Text('右')
}
.width('100%')

设置最小宽度:

typescript 复制代码
Row() {
  Text('左')
  Blank()
    .minWidth(20)  // 最小宽度
  Text('右')
}
.width('100%')

完整代码解析

数据结构定义

typescript 复制代码
// 定义待办事项接口
interface TodoItem {
  id: number;         // 唯一标识
  text: string;       // 任务内容
  completed: boolean;  // 是否完成
  createdAt: string;   // 创建时间
}

页面组件定义

typescript 复制代码
@Entry
@Component
struct Index {
  // 状态变量
  @State todos: TodoItem[] = [];      // 待办事项数组
  @State newTodoText: string = '';    // 新任务输入
  @State nextId: number = 1;          // 下一个ID
  @State filter: number = 0;          // 筛选条件

  build() {
    Column() {
      // 头部区域
      this.HeaderSection()
      
      // 输入区域
      this.InputSection()
      
      // 筛选标签
      this.FilterSection()
      
      // 任务列表
      this.TodoListSection()
      
      // 底部统计
      this.FooterSection()
    }
    .padding(16)
    .width('100%')
    .height('100%')
    .backgroundColor('#F8F9FA')
  }
}

头部区域

typescript 复制代码
@Builder HeaderSection() {
  Row() {
    Column() {
      Text('待办事项')
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#111827')

      Text(`${this.todos.filter(t => !t.completed).length} 项待完成`)
        .fontSize(12)
        .fontColor('#6B7280')
        .margin({ top: 4 })
    }
    .alignItems(HorizontalAlign.Start)

    Blank()

    // 统计徽章
    Text(`${this.todos.filter(t => t.completed).length}/${this.todos.length}`)
      .fontSize(14)
      .fontWeight(FontWeight.Medium)
      .fontColor('#6366F1')
      .padding({ left: 16, right: 16, top: 8, bottom: 8 })
      .backgroundColor('#E0E7FF')
      .borderRadius(999)
  }
  .width('100%')
  .padding({ bottom: 24 })
}

输入区域

typescript 复制代码
@Builder InputSection() {
  Row() {
    TextInput({ placeholder: '添加新任务...', text: this.newTodoText })
      .layoutWeight(1)
      .height(52)
      .fontSize(16)
      .backgroundColor('#FFFFFF')
      .borderRadius(16)
      .onChange((value: string) => {
        this.newTodoText = value;
      })

    Button('+')
      .width(52)
      .height(52)
      .fontSize(24)
      .fontWeight(FontWeight.Bold)
      .backgroundColor('#6366F1')
      .fontColor('#FFFFFF')
      .borderRadius(16)
      .margin({ left: 8 })
      .onClick(() => {
        this.addTodo();
      })
  }
  .width('100%')
  .margin({ bottom: 16 })
}

筛选标签

typescript 复制代码
@Builder FilterSection() {
  Row() {
    Text('全部')
      .fontSize(14)
      .fontWeight(this.filter === 0 ? FontWeight.Medium : FontWeight.Regular)
      .fontColor(this.filter === 0 ? '#6366F1' : '#6B7280')
      .padding({ left: 16, right: 16, top: 8, bottom: 8 })
      .backgroundColor(this.filter === 0 ? '#E0E7FF' : '#FFFFFF')
      .borderRadius(8)
      .layoutWeight(1)
      .textAlign(TextAlign.Center)
      .onClick(() => { this.filter = 0 })

    Text('进行中')
      .fontSize(14)
      .fontWeight(this.filter === 1 ? FontWeight.Medium : FontWeight.Regular)
      .fontColor(this.filter === 1 ? '#6366F1' : '#6B7280')
      .padding({ left: 16, right: 16, top: 8, bottom: 8 })
      .backgroundColor(this.filter === 1 ? '#E0E7FF' : '#FFFFFF')
      .borderRadius(8)
      .layoutWeight(1)
      .textAlign(TextAlign.Center)
      .onClick(() => { this.filter = 1 })

    Text('已完成')
      .fontSize(14)
      .fontWeight(this.filter === 2 ? FontWeight.Medium : FontWeight.Regular)
      .fontColor(this.filter === 2 ? '#6366F1' : '#6B7280')
      .padding({ left: 16, right: 16, top: 8, bottom: 8 })
      .backgroundColor(this.filter === 2 ? '#E0E7FF' : '#FFFFFF')
      .borderRadius(8)
      .layoutWeight(1)
      .textAlign(TextAlign.Center)
      .onClick(() => { this.filter = 2 })
  }
  .width('100%')
  .backgroundColor('#FFFFFF')
  .borderRadius(12)
  .padding(4)
  .margin({ bottom: 16 })
}

任务列表

typescript 复制代码
@Builder TodoListSection() {
  if (this.getFilteredTodos().length === 0) {
    // 空状态
    Column() {
      Text(this.filter === 0 ? '暂无任务' : this.filter === 1 ? '没有进行中的任务' : '没有已完成的任务')
        .fontSize(16)
        .fontColor('#9CA3AF')
        .margin({ bottom: 8 })

      if (this.filter === 0) {
        Text('点击上方输入框添加新任务')
          .fontSize(12)
          .fontColor('#9CA3AF')
      }
    }
    .layoutWeight(1)
    .justifyContent(FlexAlign.Center)
  } else {
    // 任务列表
    List() {
      ForEach(this.getFilteredTodos(), (todo: TodoItem) => {
        ListItem() {
          this.TodoItemComponent(todo)
        }
        .margin({ bottom: 8 })
      }, (todo: TodoItem) => todo.id.toString())
    }
    .layoutWeight(1)
    .width('100%')
  }
}

任务项组件

typescript 复制代码
@Builder TodoItemComponent(todo: TodoItem) {
  Row() {
    Checkbox()
      .select(todo.completed)
      .selectedColor('#6366F1')
      .onChange((value: boolean) => {
        const index = this.todos.findIndex(t => t.id === todo.id);
        if (index >= 0) {
          this.todos[index].completed = value;
        }
      })

    Column() {
      Text(todo.text)
        .fontSize(16)
        .fontWeight(todo.completed ? FontWeight.Regular : FontWeight.Medium)
        .fontColor(todo.completed ? '#9CA3AF' : '#111827')
        .decoration({
          type: todo.completed ? TextDecorationType.LineThrough : TextDecorationType.None
        })

      Text(todo.createdAt)
        .fontSize(12)
        .fontColor('#9CA3AF')
        .margin({ top: 4 })
    }
    .layoutWeight(1)
    .margin({ left: 8 })
    .alignItems(HorizontalAlign.Start)

    Button('删除')
      .height(32)
      .fontSize(12)
      .backgroundColor('#FEE2E2')
      .fontColor('#EF4444')
      .borderRadius(8)
      .onClick(() => {
        this.todos = this.todos.filter(t => t.id !== todo.id);
      })
  }
  .width('100%')
  .padding(16)
  .backgroundColor('#FFFFFF')
  .borderRadius(12)
}

底部统计

typescript 复制代码
@Builder FooterSection() {
  if (this.todos.length > 0) {
    Row() {
      Text(`共 ${this.todos.length} 项`)
        .fontSize(12)
        .fontColor('#9CA3AF')

      Blank()

      Button('清除已完成')
        .fontSize(12)
        .height(32)
        .backgroundColor('#FEE2E2')
        .fontColor('#EF4444')
        .borderRadius(8)
        .onClick(() => {
          this.todos = this.todos.filter(t => !t.completed);
        })
    }
    .width('100%')
    .padding({ top: 16 })
  }
}

核心方法

typescript 复制代码
// 添加新任务
addTodo(): void {
  if (this.newTodoText.trim()) {
    this.todos.push({
      id: this.nextId++,
      text: this.newTodoText.trim(),
      completed: false,
      createdAt: new Date().toLocaleDateString()
    });
    this.newTodoText = '';
  }
}

// 获取筛选后的任务
getFilteredTodos(): TodoItem[] {
  switch (this.filter) {
    case 1:
      return this.todos.filter(t => !t.completed);
    case 2:
      return this.todos.filter(t => t.completed);
    default:
      return this.todos;
  }
}

常见问题与解决方案

问题1:列表性能问题

问题描述:当任务数量很多时,列表滚动可能卡顿。

解决方案

typescript 复制代码
List() {
  ForEach(this.todos, ...)
}
.cachedCount(10)  // 增加缓存数量

问题2:输入框键盘遮挡

问题描述:在小屏幕设备上,键盘可能遮挡输入框。

解决方案

typescript 复制代码
Column() {
  // 输入区域
  this.InputSection()
}
.expandSafeArea([Keyboard])  // 扩展到键盘区域

问题3:数据持久化

问题描述:应用关闭后数据丢失。

解决方案:使用 Preferences 或数据库存储数据。


扩展学习

1. 添加更多功能

  • 任务优先级
  • 任务分类
  • 提醒功能
  • 数据同步

2. 优化用户体验

  • 滑动删除
  • 长按编辑
  • 拖拽排序
  • 动画效果

3. 数据持久化

  • 使用 Preferences 存储简单数据
  • 使用 RDB 存储复杂数据
  • 使用分布式数据同步

总结

通过本教程,你学习了:

  1. interface - 接口定义数据结构
  2. @State 数组 - 管理数组类型的状态
  3. TextInput - 文本输入框组件
  4. Checkbox - 复选框组件
  5. List/ListItem - 列表组件
  6. ForEach - 循环渲染
  7. TextDecorationType - 文本装饰(删除线)
  8. 条件渲染 - if/else 语句
  9. 数组方法 - filter、findIndex、push、sort
  10. @Builder - 自定义构建函数

这些知识点构成了 HarmonyOS NEXT 中列表应用开发的基础,掌握它们后,你将能够构建更复杂的列表应用。

相关推荐
想你依然心痛1 小时前
HarmonyOS 6(API 23)智能体驱动的沉浸式AR航天器装配工坊
华为·ar·harmonyos·智能体
不羁的木木1 小时前
HarmonyOS文件基础服务(Core File Kit)实战演练05-实战:文件管理工具开发
华为·harmonyos
Goway_Hui2 小时前
【鸿蒙原生应用开发--ArkUI--007】TimerApp - 计时器应用教程
华为·harmonyos
nashane2 小时前
HarmonyOS 6学习:保存图片预览空白?沙箱路径转URI的“视觉修复”术
学习·华为·harmonyos
芒鸽2 小时前
HarmonyOS ArkTS 状态管理深度解析:@State、@Prop、@Link、@Provide/@Consume 实战指南
华为·harmonyos·arkts·状态管理
大雷神3 小时前
HarmonyOS APP<玩转React>开源教程三十一:示例项目下载功能
react.js·开源·harmonyos
想你依然心痛3 小时前
HarmonyOS 6(API 23)智能体驱动的沉浸式AR量子计算实验室
ar·harmonyos·量子计算·智能体
技术路线图3 小时前
鸿蒙系统小红书应用分身设置教程(2026详细版)
华为·harmonyos
科技与数码3 小时前
鸿蒙智能体框架HMAF与智能化升级全解析
华为·harmonyos