🎯 目标

构建一个文件拖拽上传组件 DragUploader,适用于:

  • 拖入文件上传(本地图片 / 文档 / 视频等)
  • 支持类型 / 大小校验,上传前预处理
  • 拖入时区域高亮,提示用户释放文件
  • 文件成功回调(支持单文件 / 多文件)
  • 支持嵌套使用或独立上传区使用

🧱 样式交互示意

[ 🗂 将文件拖入此区域上传 ]
【拖入中】→ 边框高亮 + 图标动画提示
【上传完成】→ 显示文件名 / 处理回调

🧰 组件实现:DragUploader.ets

@Component
export struct DragUploader {
  @Prop accept: Array<string> = ['image/png', 'image/jpeg', 'application/pdf']
  @Prop maxSizeMB: number = 10
  @Prop multiple: boolean = false
  @Prop onUpload: (files: Array<{ name: string, type: string, content: string }>) => void = () => {}

  @State isDragging: boolean = false

  build() {
    Column()
      .height(160)
      .border({ width: 2, color: this.isDragging ? '#007DFF' : '#ccc', style: BorderStyle.Dashed })
      .borderRadius(12)
      .alignItems(HorizontalAlign.Center)
      .justifyContent(FlexAlign.Center)
      .onDragEnter(() => this.isDragging = true)
      .onDragLeave(() => this.isDragging = false)
      .onDrop(async event => {
        this.isDragging = false
        const files = await this.parseFiles(event.dataTransfer.files)
        this.onUpload(files)
      }) {
      Column({ space: 8 }).alignItems(HorizontalAlign.Center) {
        Image($r('app.media.icon_upload')).width(40).height(40)
        Text(this.isDragging ? '松开上传文件' : '将文件拖入此区域上传').fontSize(14).fontColor('#666')
      }
    }
  }

  private async parseFiles(fileList: FileList): Promise<Array<{ name: string, type: string, content: string }>> {
    const files: Array<{ name: string, type: string, content: string }> = []

    for (let i = 0; i < fileList.length; i++) {
      const file = fileList[i]
      if (!this.accept.includes(file.type)) continue
      if (file.size / 1024 / 1024 > this.maxSizeMB) continue

      const content = await this.readFileAsBase64(file)
      files.push({
        name: file.name,
        type: file.type,
        content
      })

      if (!this.multiple) break
    }

    return files
  }

  private readFileAsBase64(file: File): Promise<string> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader()
      reader.onload = () => resolve(reader.result as string)
      reader.onerror = reject
      reader.readAsDataURL(file)
    })
  }
}

📦 使用示例

@Entry
@Component
struct DemoDragUploader {
  @State uploaded: string = ''

  build() {
    Column({ space: 20 }) {
      DragUploader({
        accept: ['image/png', 'image/jpeg'],
        multiple: false,
        onUpload: files => {
          this.uploaded = files.length > 0 ? `上传成功:${files[0].name}` : '无有效文件'
        }
      })

      if (this.uploaded) {
        Text(this.uploaded).fontSize(14).fontColor('#007DFF')
      }
    }.padding(20)
  }
}

✨ 可扩展能力建议

功能 说明
拖拽动画反馈 拖入区域边框变色 / 图标放大 / 文案变更
上传进度条显示 支持异步上传接口时显示上传百分比
上传后回显文件名 / 缩略图 图片 / 文档上传后显示缩略内容
拖拽禁用区域限制 限制可拖入区域范围,避免页面误触

📘 下一篇预告

第40篇:【HarmonyOS 5.0.0 或以上】构建图表组件 ChartCard:集成折线图 / 柱状图 / 饼图 / 动态数据渲染

Logo

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

更多推荐