HarmonyOS应用<节气通>开发第17篇:意见反馈页面

引言

意见反馈页面是用户与开发者沟通的重要渠道,帮助用户报告问题、提出建议。本文将实现一个功能完善的意见反馈页面,包括:

  • 反馈表单
  • 问题分类选择
  • 截图上传
  • 提交反馈

通过本文,你将掌握如何构建一个用户反馈系统。


学习目标

完成本文后,你将能够:

  • ✅ 实现反馈表单
  • ✅ 添加问题分类
  • ✅ 实现图片上传
  • ✅ 提交反馈功能
  • ✅ 处理表单验证

需求分析

功能模块设计

模块 功能描述 技术要点
问题分类 选择反馈类型 下拉选择、按钮组
反馈内容 输入反馈详情 TextArea组件
图片上传 上传截图 图片选择器
联系方式 输入邮箱/电话 TextInput组件
提交按钮 提交反馈 表单验证

设计思路

方案对比

方案 优点 缺点 适用场景
单页表单 操作流程短,用户一次完成 内容较多时页面较长 推荐 - 适合中等复杂度表单
分步表单 步骤清晰,降低认知负担 操作流程长,需要多步跳转 表单字段非常多(>10个)
弹窗表单 不跳转页面,保持上下文 空间有限,不适合复杂表单 简单反馈或快速操作
抽屉表单 平滑展开,不离开当前页面 可用空间有限 需要快速反馈但不离开主页面

关键决策

决策1: 使用按钮组选择反馈类型

  • 原因:反馈类型较少(4种),按钮组展示更直观
  • 优势:用户一眼就能看到所有选项,无需点击展开
  • 技术实现:使用 Grid 布局,四列展示,选中状态高亮

决策2: 限制图片上传数量(最多3张)

  • 原因:避免用户上传过多图片影响性能和服务器存储
  • 优势:控制服务器存储压力,提升页面加载速度
  • 考虑因素:
    • 单张图片最大尺寸限制(如5MB)
    • 支持的图片格式(jpg、png)
    • 图片压缩处理

决策3: 表单验证在提交时进行

  • 原因:减少用户输入时的干扰,避免频繁提示
  • 优势:用户体验更流畅,专注于输入内容
  • 验证时机:点击提交按钮时统一验证

决策4: 联系方式格式验证

  • 原因:确保能联系到用户进行反馈回复
  • 优势:提高反馈处理效率
  • 验证规则:支持邮箱或手机号格式

架构设计

typescript 复制代码
// 反馈类型定义
interface FeedbackType {
  id: string;        // 类型标识
  name: string;      // 显示名称
  icon: Resource;    // 图标资源
}

// 表单状态管理
@State selectedType: string = 'bug';     // 选中的反馈类型
@State content: string = '';             // 反馈内容
@State images: string[] = [];            // 上传的图片列表
@State contact: string = '';             // 联系方式
@State isSubmitting: boolean = false;    // 提交状态

图片上传策略

策略 说明 适用场景
立即上传 选择图片后立即上传到服务器 需要实时预览
延迟上传 选择图片后暂存本地,提交时一起上传 推荐 - 减少请求次数
压缩上传 上传前压缩图片 移动端网络不稳定时
异步上传 后台异步上传,不阻塞表单提交 大文件或多张图片

提交流程设计

复制代码
用户填写表单
    ↓
点击提交按钮
    ↓
表单验证(内容、联系方式)
    ↓
显示加载状态(禁用按钮)
    ↓
构建请求数据(类型、内容、图片、联系方式)
    ↓
发送HTTP请求
    ↓
处理响应
    ↓
显示结果提示(成功/失败)
    ↓
重置表单或返回上一页

核心实现

步骤1: 页面结构设计

完整代码
typescript 复制代码
// pages/Feedback.ets

import router from '@ohos.router';
import prompt from '@ohos.prompt';

@Entry
@Component
struct Feedback {
  // 反馈类型
  @State selectedType: string = 'bug';
  
  // 反馈内容
  @State content: string = '';
  
  // 上传的图片
  @State images: string[] = [];
  
  // 联系方式
  @State contact: string = '';
  
  // 反馈类型列表
  private feedbackTypes: FeedbackType[] = [
    { id: 'bug', name: 'Bug报告', icon: $r('app.media.ic_bug') },
    { id: 'feature', name: '功能建议', icon: $r('app.media.ic_feature') },
    { id: 'improvement', name: '体验优化', icon: $r('app.media.ic_improve') },
    { id: 'other', name: '其他', icon: $r('app.media.ic_other') }
  ];
  
  /**
   * 提交反馈
   */
  submitFeedback(): void {
    // 表单验证
    if (!this.content.trim()) {
      prompt.showToast({ message: '请输入反馈内容' });
      return;
    }
    
    if (!this.contact.trim()) {
      prompt.showToast({ message: '请输入联系方式' });
      return;
    }
    
    // 模拟提交
    prompt.showToast({ message: '反馈提交成功,感谢您的意见!' });
    
    // 重置表单
    this.selectedType = 'bug';
    this.content = '';
    this.images = [];
    this.contact = '';
    
    // 返回上一页
    setTimeout(() => {
      try {
        router.back();
      } catch (error) {
        console.error('返回失败: ' + JSON.stringify(error));
      }
    }, 1500);
  }
  
  /**
   * 添加图片
   */
  addImage(): void {
    if (this.images.length >= 3) {
      prompt.showToast({ message: '最多上传3张图片' });
      return;
    }
    
    // 模拟选择图片
    const mockImage = 'image_' + Date.now() + '.png';
    this.images.push(mockImage);
    prompt.showToast({ message: '图片上传成功' });
  }
  
  /**
   * 删除图片
   */
  removeImage(index: number): void {
    this.images.splice(index, 1);
  }
  
  /**
   * 构建UI
   */
  build() {
    Scroll() {
      Column({ space: 0 }) {
        // 1. 顶部导航
        this.buildHeader()
        
        // 2. 反馈类型选择
        this.buildTypeSelector()
        
        // 3. 反馈内容
        this.buildContentInput()
        
        // 4. 图片上传
        this.buildImageUpload()
        
        // 5. 联系方式
        this.buildContactInput()
        
        // 6. 提交按钮
        this.buildSubmitButton()
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F8F7F2')
  }
}

interface FeedbackType {
  id: string;
  name: string;
  icon: Resource;
}
代码解析

1. 状态管理

  • selectedType: 选中的反馈类型
  • content: 反馈内容
  • images: 上传的图片列表
  • contact: 联系方式

2. 功能方法

  • submitFeedback(): 提交反馈(含表单验证)
  • addImage(): 添加图片
  • removeImage(): 删除图片

步骤2: 顶部导航

typescript 复制代码
/**
 * 构建顶部导航
 */
@Builder
buildHeader(): void {
  Row({ space: 16 }) {
    Image($r('app.media.ic_back'))
      .width(24)
      .height(24)
      .fillColor('#333333')
      .onClick(() => {
        try {
          router.back();
        } catch (error) {
          console.error('返回失败: ' + JSON.stringify(error));
        }
      })
    
    Text('意见反馈')
      .fontSize(18)
      .fontWeight(FontWeight.Bold)
      .fontColor('#333333')
    
    Blank()
  }
  .width('100%')
  .height(56)
  .padding({ left: 16, right: 16 })
  .backgroundColor('#FFFFFF')
}

设计要点:

  • 返回按钮
  • 标题居中
  • 简洁的导航栏

步骤3: 反馈类型选择

typescript 复制代码
/**
 * 构建反馈类型选择器
 */
@Builder
buildTypeSelector(): void {
  Card() {
    Column({ space: 12 }) {
      // 标题
      Text('问题分类')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor('#333333')
      
      // 类型按钮组
      Grid() {
        ForEach(this.feedbackTypes, (type: FeedbackType) => {
          GridItem() {
            this.buildTypeButton(type)
          }
        }, (type: FeedbackType) => type.id)
      }
      .columnsTemplate('1fr 1fr 1fr 1fr')
      .rowsGap(8)
    }
    .padding(16)
  }
  .width('92%')
  .margin({ left: '4%', right: '4%', top: 12 })
}

/**
 * 构建类型按钮
 */
@Builder
buildTypeButton(type: FeedbackType): void {
  const isSelected = this.selectedType === type.id;
  
  Column({ space: 6 }) {
    Stack({ alignContent: Alignment.Center }) {
      Circle()
        .width(40)
        .height(40)
        .fillColor(isSelected ? '#4A9B6D' : '#F5F5F5')
      
      Image(type.icon)
        .width(20)
        .height(20)
        .fillColor(isSelected ? '#FFFFFF' : '#999999')
    }
    
    Text(type.name)
      .fontSize(12)
      .fontColor(isSelected ? '#4A9B6D' : '#666666')
  }
  .width('100%')
  .alignItems(HorizontalAlign.Center)
  .onClick(() => {
    this.selectedType = type.id;
  })
}

设计要点:

  • 四列网格布局
  • 圆形图标+文字
  • 选中状态高亮

步骤4: 反馈内容输入

typescript 复制代码
/**
 * 构建反馈内容输入框
 */
@Builder
buildContentInput(): void {
  Card() {
    Column({ space: 8 }) {
      // 标题
      Row({ space: 8 }) {
        Text('反馈内容')
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
        
        Text('*')
          .fontSize(14)
          .fontColor('#FF5252')
      }
      
      // 输入框
      TextArea({ placeholder: '请详细描述您的问题或建议...' })
        .width('100%')
        .height(120)
        .backgroundColor('#F5F5F5')
        .borderRadius(8)
        .padding(12)
        .fontSize(14)
        .onChange((value: string) => {
          this.content = value;
        })
      
      // 字数统计
      Row() {
        Blank()
        
        Text(this.content.length + '/500')
          .fontSize(12)
          .fontColor('#999999')
      }
    }
    .padding(16)
  }
  .width('92%')
  .margin({ left: '4%', right: '4%', top: 12 })
}

设计要点:

  • TextArea多行输入
  • 字数统计
  • 占位提示文字

步骤5: 图片上传

typescript 复制代码
/**
 * 构建图片上传区域
 */
@Builder
buildImageUpload(): void {
  Card() {
    Column({ space: 12 }) {
      // 标题
      Text('上传图片(可选)')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor('#333333')
      
      // 图片列表
      Row({ space: 12 }) {
        ForEach(this.images, (image: string, index: number) => {
          Stack({ alignContent: Alignment.TopEnd }) {
            Image('rawfile://feedback/' + image)
              .width(80)
              .height(80)
              .borderRadius(8)
              .objectFit(ImageFit.Cover)
            
            Image($r('app.media.ic_delete'))
              .width(20)
              .height(20)
              .fillColor('#FFFFFF')
              .backgroundColor('#00000080')
              .borderRadius(10)
              .padding(4)
              .onClick(() => {
                this.removeImage(index);
              })
          }
        })
        
        // 添加图片按钮
        if (this.images.length < 3) {
          Column({ space: 8 }) {
            Image($r('app.media.ic_add_image'))
              .width(32)
              .height(32)
              .fillColor('#CCCCCC')
            
            Text('添加图片')
              .fontSize(12)
              .fontColor('#999999')
          }
          .width(80)
          .height(80)
          .backgroundColor('#F5F5F5')
          .borderRadius(8)
          .border({ width: 1, color: '#EEEEEE', style: BorderStyle.Dashed })
          .onClick(() => {
            this.addImage();
          })
        }
      }
      
      // 提示文字
      Text('最多上传3张图片,支持jpg、png格式')
        .fontSize(12)
        .fontColor('#999999')
    }
    .padding(16)
  }
  .width('92%')
  .margin({ left: '4%', right: '4%', top: 12 })
}

设计要点:

  • 图片列表展示
  • 删除图片按钮
  • 添加图片按钮(虚线边框)
  • 上传数量限制提示

步骤6: 联系方式输入

typescript 复制代码
/**
 * 构建联系方式输入框
 */
@Builder
buildContactInput(): void {
  Card() {
    Column({ space: 8 }) {
      // 标题
      Row({ space: 8 }) {
        Text('联系方式')
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
        
        Text('*')
          .fontSize(14)
          .fontColor('#FF5252')
      }
      
      // 输入框
      Row({ space: 12 }) {
        Image($r('app.media.ic_contact'))
          .width(20)
          .height(20)
          .fillColor('#999999')
        
        TextInput({ placeholder: '请输入邮箱或手机号,以便我们回复您' })
          .width('100%')
          .height(40)
          .backgroundColor('#F5F5F5')
          .borderRadius(8)
          .padding({ left: 12 })
          .fontSize(14)
          .onChange((value: string) => {
            this.contact = value;
          })
      }
    }
    .padding(16)
  }
  .width('92%')
  .margin({ left: '4%', right: '4%', top: 12 })
}

设计要点:

  • 图标+输入框布局
  • 占位提示文字

步骤7: 提交按钮

typescript 复制代码
/**
 * 构建提交按钮
 */
@Builder
buildSubmitButton(): void {
  Button('提交反馈')
    .width('92%')
    .height(48)
    .backgroundColor(this.content.trim() && this.contact.trim() ? '#4A9B6D' : '#DDDDDD')
    .fontColor('#FFFFFF')
    .fontSize(16)
    .fontWeight(FontWeight.Medium)
    .borderRadius(24)
    .margin({ top: 24, bottom: 100 })
    .onClick(() => {
      this.submitFeedback();
    })
}

设计要点:

  • 圆角按钮
  • 根据表单状态禁用/启用
  • 点击提交反馈

常见问题与解决方案

问题1: 表单验证不生效

现象 :

点击提交按钮时,表单验证没有执行,直接提交了空表单。

原因分析:

  • 按钮点击事件没有正确绑定到验证方法
  • 验证逻辑存在语法错误
  • 状态变量名拼写错误

解决方案:

typescript 复制代码
// ✅ 正确的表单验证流程
submitFeedback(): void {
  // 1. 验证反馈内容
  if (!this.content.trim()) {
    prompt.showToast({ message: '请输入反馈内容' });
    return;
  }
  
  // 2. 验证反馈内容长度
  if (this.content.length > 500) {
    prompt.showToast({ message: '反馈内容不能超过500字' });
    return;
  }
  
  // 3. 验证联系方式必填
  if (!this.contact.trim()) {
    prompt.showToast({ message: '请输入联系方式' });
    return;
  }
  
  // 4. 验证联系方式格式
  if (!this.isValidContact(this.contact)) {
    prompt.showToast({ message: '请输入有效的邮箱或手机号' });
    return;
  }
  
  // 5. 执行提交
  this.performSubmit();
}

// 联系方式验证方法
isValidContact(contact: string): boolean {
  // 邮箱格式验证
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  // 手机号格式验证(中国手机号)
  const phoneRegex = /^1[3-9]\d{9}$/;
  
  return emailRegex.test(contact) || phoneRegex.test(contact);
}

调试技巧:

typescript 复制代码
// 在验证方法中添加日志
submitFeedback(): void {
  console.log('content:', this.content);
  console.log('contact:', this.contact);
  console.log('isValidContact:', this.isValidContact(this.contact));
  // ...
}

问题2: 图片上传数量限制不生效

现象 :

可以上传超过3张图片,数量限制没有起作用。

原因分析:

  • 添加图片时没有检查当前图片数量
  • 检查逻辑位置错误(在选择图片后才检查)
  • 状态更新时机问题

解决方案:

typescript 复制代码
// ✅ 正确的数量限制逻辑
addImage(): void {
  // 在选择图片之前检查数量限制
  if (this.images.length >= 3) {
    prompt.showToast({ message: '最多上传3张图片' });
    return;
  }
  
  // 调用图片选择器
  // ...
}

// 显示添加按钮的条件判断
buildAddImageButton(): void {
  if (this.images.length < 3) {
    Column({ space: 8 }) {
      Image($r('app.media.ic_add_image'))
        .width(32)
        .height(32)
        .fillColor('#CCCCCC')
      
      Text('添加图片')
        .fontSize(12)
        .fontColor('#999999')
    }
    .width(80)
    .height(80)
    .backgroundColor('#F5F5F5')
    .borderRadius(8)
    .border({ width: 1, color: '#EEEEEE', style: BorderStyle.Dashed })
    .onClick(() => {
      this.addImage();
    })
  }
}

问题3: 删除图片时触发父元素点击

现象 :

点击删除图片按钮时,同时触发了添加图片按钮或其他父元素的点击事件。

原因分析:

  • 事件冒泡机制导致点击事件向上传递
  • 删除按钮没有阻止事件冒泡

解决方案:

typescript 复制代码
// ✅ 阻止事件冒泡
Image($r('app.media.ic_delete'))
  .width(20)
  .height(20)
  .fillColor('#FFFFFF')
  .backgroundColor('#00000080')
  .borderRadius(10)
  .padding(4)
  .onClick((event: ClickEvent) => {
    event.stopPropagation();  // 阻止事件冒泡
    this.removeImage(index);
  })

最佳实践:

typescript 复制代码
// 封装通用的阻止冒泡方法
stopEvent(event: ClickEvent): void {
  event.stopPropagation();
  event.preventDefault();
}

// 使用
Image($r('app.media.ic_delete'))
  .onClick((event: ClickEvent) => {
    this.stopEvent(event);
    this.removeImage(index);
  })

问题4: 按钮状态不随表单变化

现象 :

表单填写完成后,提交按钮仍然显示为禁用状态(灰色)。

原因分析:

  • 按钮背景色判断逻辑有问题
  • 状态更新不及时
  • 条件判断遗漏了某些字段

解决方案:

typescript 复制代码
// ✅ 正确的按钮状态逻辑
Button('提交反馈')
  .width('92%')
  .height(48)
  .backgroundColor(this.canSubmit ? '#4A9B6D' : '#DDDDDD')
  .fontColor('#FFFFFF')
  .fontSize(16)
  .fontWeight(FontWeight.Medium)
  .borderRadius(24)
  .margin({ top: 24, bottom: 100 })
  .onClick(() => {
    if (this.canSubmit) {
      this.submitFeedback();
    }
  })

// 计算属性判断是否可以提交
get canSubmit(): boolean {
  return this.content.trim().length > 0 && 
         this.contact.trim().length > 0 &&
         this.isValidContact(this.contact) &&
         !this.isSubmitting;
}

状态更新优化:

typescript 复制代码
// 添加提交状态防止重复提交
@State isSubmitting: boolean = false;

submitFeedback(): void {
  if (this.isSubmitting) return;
  
  this.isSubmitting = true;
  
  // 提交逻辑...
  
  // 提交完成后恢复状态
  this.isSubmitting = false;
}

问题5: 反馈内容字数统计不正确

现象 :

字数统计显示与实际输入不符,或者统计不更新。

原因分析:

  • onChange 回调没有正确获取输入值
  • 状态更新有延迟
  • TextArea 没有正确绑定状态

解决方案:

typescript 复制代码
// ✅ 正确的字数统计
TextArea({ placeholder: '请详细描述您的问题或建议...' })
  .width('100%')
  .height(120)
  .backgroundColor('#F5F5F5')
  .borderRadius(8)
  .padding(12)
  .fontSize(14)
  .maxLength(500)  // 设置最大长度
  .onChange((value: string) => {
    this.content = value;  // 实时更新状态
  })

// 显示字数(使用状态变量)
Text(this.content.length + '/500')
  .fontSize(12)
  .fontColor(this.content.length > 500 ? '#FF5252' : '#999999')

边界情况处理:

typescript 复制代码
// 处理超过最大长度的情况
Text(this.content.length > 500 ? '已超出限制' : this.content.length + '/500')
  .fontSize(12)
  .fontColor(this.content.length > 500 ? '#FF5252' : '#999999')

问题6: 图片选择器调用失败

现象 :

点击添加图片按钮没有反应,无法选择图片。

原因分析:

  • 权限配置缺失
  • 图片选择器API调用错误
  • 设备不支持图片选择

解决方案:

typescript 复制代码
// ✅ 完整的图片选择流程
async addImage(): Promise<void> {
  if (this.images.length >= 3) {
    prompt.showToast({ message: '最多上传3张图片' });
    return;
  }
  
  try {
    // 调用系统图片选择器
    const result = await imagePicker.select({
      count: 1,
      mediaType: imagePicker.MediaType.Image
    });
    
    if (result && result.photos && result.photos.length > 0) {
      const imageUri = result.photos[0].uri;
      this.images.push(imageUri);
      prompt.showToast({ message: '图片添加成功' });
    }
  } catch (error) {
    console.error('图片选择失败: ', error);
    prompt.showToast({ message: '图片选择失败,请重试' });
  }
}

权限配置:

json 复制代码
// module.json5 中添加权限
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.READ_MEDIA",
        "reason": "用于选择图片"
      }
    ]
  }
}

问题7: 提交按钮重复点击

现象 :

快速点击提交按钮多次,导致重复提交反馈。

原因分析:

  • 没有防止重复提交的机制
  • 网络请求较慢时用户可能多次点击

解决方案:

typescript 复制代码
// ✅ 防止重复提交
@State isSubmitting: boolean = false;

submitFeedback(): void {
  // 检查是否正在提交
  if (this.isSubmitting) {
    return;
  }
  
  // 表单验证
  if (!this.content.trim()) {
    prompt.showToast({ message: '请输入反馈内容' });
    return;
  }
  
  if (!this.contact.trim()) {
    prompt.showToast({ message: '请输入联系方式' });
    return;
  }
  
  // 设置提交状态
  this.isSubmitting = true;
  
  // 模拟提交
  setTimeout(() => {
    prompt.showToast({ message: '反馈提交成功,感谢您的意见!' });
    
    // 重置表单
    this.resetForm();
    
    // 恢复提交状态
    this.isSubmitting = false;
    
    // 返回上一页
    setTimeout(() => {
      router.back();
    }, 1500);
  }, 1000);
}

// 重置表单
resetForm(): void {
  this.selectedType = 'bug';
  this.content = '';
  this.images = [];
  this.contact = '';
}

按钮状态绑定:

typescript 复制代码
Button(this.isSubmitting ? '提交中...' : '提交反馈')
  .backgroundColor(this.canSubmit && !this.isSubmitting ? '#4A9B6D' : '#DDDDDD')
  .enabled(!this.isSubmitting)

问题8: 反馈类型选择后状态不更新

现象 :

点击反馈类型按钮后,选中状态没有变化。

原因分析:

  • 状态绑定错误
  • 点击事件没有正确更新状态
  • 样式判断逻辑错误

解决方案:

typescript 复制代码
// ✅ 正确的类型选择逻辑
@Builder
buildTypeButton(type: FeedbackType): void {
  const isSelected = this.selectedType === type.id;
  
  Column({ space: 6 }) {
    Stack({ alignContent: Alignment.Center }) {
      Circle()
        .width(40)
        .height(40)
        .fillColor(isSelected ? '#4A9B6D' : '#F5F5F5')
      
      Image(type.icon)
        .width(20)
        .height(20)
        .fillColor(isSelected ? '#FFFFFF' : '#999999')
    }
    
    Text(type.name)
      .fontSize(12)
      .fontColor(isSelected ? '#4A9B6D' : '#666666')
  }
  .width('100%')
  .alignItems(HorizontalAlign.Center)
  .onClick(() => {
    // 更新选中状态
    this.selectedType = type.id;
    console.log('selectedType:', this.selectedType);  // 调试日志
  })
}

调试方法:

typescript 复制代码
// 在点击事件中添加日志
onClick(() => {
  console.log('Before:', this.selectedType);
  this.selectedType = type.id;
  console.log('After:', this.selectedType);
})

本章小结

核心知识点

本文完成了意见反馈页面的实现:

1. 反馈类型选择

  • 四种反馈类型:Bug报告、功能建议、体验优化、其他
  • 图标+文字展示
  • 选中状态高亮

2. 反馈内容输入

  • TextArea多行输入
  • 字数统计(最多500字)
  • 占位提示文字

3. 图片上传

  • 最多上传3张图片
  • 删除已上传图片
  • 添加图片按钮

4. 联系方式

  • 邮箱或手机号输入
  • 表单验证必填

5. 提交按钮

  • 根据表单状态禁用/启用
  • 点击提交反馈

下一步预告

意见反馈页面已经完成!在下一篇文章中,我们将学习:

  • 关于页面与隐私政策
  • 应用介绍
  • 隐私政策展示
  • 用户协议展示

节气通应用已发布上线,可在应用市场下载体验


相关链接

相关推荐
yuegu7773 小时前
HarmonyOS应用<节气通>开发第19篇:空态页面设计
harmonyos
伶俜663 小时前
零基础学 ArkUI 传感器(专题二):从加速度计到指南针,玩转硬件能力
学习·华为·harmonyos
G_dou_3 小时前
Flutter三方库适配OpenHarmony【expense_tracker】消费记录器项目完整实战
flutter·harmonyos
FrameNotWork4 小时前
HarmonyOS6.1 从图像分类到目标检测的扩展实现
人工智能·harmonyos
nashane5 小时前
HarmonyOS 6商城开发学习:消息中心未读清零——@ObservedV2+@Trace驱动一键清除
学习·华为·harmonyos
yuegu7775 小时前
HarmonyOS应用<节气通>开发第16篇:知识问答页面
华为·harmonyos
●VON5 小时前
AtomGit Flutter鸿蒙客户端:鸿蒙平台集成
flutter·华为·跨平台·harmonyos·鸿蒙
weixin_604236676 小时前
华三 二层交换机 企业完整正式版配置
运维·网络·华为·华为交换机命令