SpaceManagePage:空间分类 + 进度条 + 清理缓存

SpaceManagePage:空间分类 + 进度条 + 清理缓存

页面预览

前言

随着用户使用「海风日记」的时间增长,App 占用空间会越来越大——文字日记、图片附件、缩略图缓存……如何让用户一目了然地看到空间使用情况,并提供一键清理的能力?

SpaceManagePage(空间管理页)就是回答这个问题的页面。它由三部分组成:

  1. 总览进度条 —— 显示已用/总容量的比例
  2. 空间分类列表 —— 文字日记 / 图片附件 / 其他,各自占用多少
  3. 清理缓存按钮 —— 一键清理临时文件

本文将从 SpaceManagePage.ets 源码出发,深入讲解:

  1. 空间分类数据的获取与展示
  2. 单行布局的视觉层级(色块 + 标签 + 大小 + 百分比)
  3. 进度条在空间管理页的特殊样式
  4. 清理缓存的二次确认与执行
  5. DataManagePage 的协作关系

好的空间管理页,应当让用户「3 秒内看懂自己占了多少」。


一、页面整体结构

@Entry
@Component
struct SpaceManagePage {
  @State totalSize: string = '45 MB / 128 MB'
  @State usedPct: number = 35
  @State categories: CategoryItem[] = [
    { label: '文字日记', size: '12 MB', color: '#F5A623', pct: 26 },
    { label: '图片附件', size: '28 MB', color: '#C8956A', pct: 62 },
    { label: '其他', size: '5 MB', color: '#E8D5B0', pct: 12 }
  ]

  build() {
    Column() {
      this.NavBarBuilder()
      Scroll() {
        Column({ space: 20 }) {
          this.TotalProgressBuilder()      // 1. 总览进度条
          this.CategoryListBuilder()       // 2. 空间分类列表
          this.ClearCacheButtonBuilder()   // 3. 清理缓存按钮
        }
        .padding({ left: 16, right: 16, top: 20, bottom: 40 })
      }
      .layoutWeight(1).scrollBar(BarState.Off)
    }
    .height('100%').backgroundColor(COLOR_BG)
  }
}

1.1 页面结构图

┌────────────────────────────┐
│  ‹ 空间管理                 │ ← 导航栏
├────────────────────────────┤
│  45 MB / 128 MB             │
│  ▓▓▓▓▓░░░░░░░░░░░ 35%      │ ← 总览进度条
├────────────────────────────┤
│  ■ 文字日记      12 MB 26%  │
│  ■ 图片附件      28 MB 62%  │ ← 空间分类
│  ■ 其他           5 MB 12%  │
├────────────────────────────┤
│  [      清理缓存      ]    │ ← 清理按钮
└────────────────────────────┘

二、总览进度条

@Builder
TotalProgressBuilder() {
  Column({ space: 12 }) {
    // 容量文字
    Row() {
      Text(this.totalSize).fontSize(14).fontColor(COLOR_TEXT_MAIN)
      Text(`${this.usedPct}%`).fontSize(14).fontColor(COLOR_AMBER)
        .margin({ left: 'auto' })
    }
    .width('100%')

    // 进度条本体
    Stack({ alignContent: Alignment.Start }) {
      Row().height(10).width('100%')
        .backgroundColor('rgba(200,180,140,0.18)').borderRadius(5)
      Row().height(10).width(`${this.usedPct}%`)
        .linearGradient({ angle: 90, colors: [['#FFD4A0', 0], [COLOR_PRIMARY, 1]] })
        .borderRadius(5)
    }
  }
  .padding(16)
  .backgroundColor(COLOR_CARD_BG)
  .borderRadius(CARD_RADIUS)
}

2.1 与「我的」页进度条的对比

属性我的页(简版)空间管理页(详版)
高度6vp10vp
圆角3vp5vp
容量文字显示
百分比文字显示

三、空间分类列表

@Builder
CategoryListBuilder() {
  Column() {
    ForEach(this.categories, (item: CategoryItem, idx: number) => {
      Row({ space: 12 }) {
        // 1. 色块
        Row()
          .width(12).height(12).borderRadius(6)
          .backgroundColor(item.color)

        // 2. 标签
        Text(item.label)
          .fontSize(14).fontColor(COLOR_TEXT_MAIN)
          .layoutWeight(1)

        // 3. 大小
        Text(item.size)
          .fontSize(13).fontColor(COLOR_TEXT_SECONDARY)

        // 4. 百分比
        Text(`${item.pct}%`)
          .fontSize(12).fontColor(COLOR_TEXT_HINT)
          .width(36).textAlign(TextAlign.End)
      }
      .width('100%').height(50)
      .padding({ left: PAGE_PADDING, right: PAGE_PADDING })

      // 分隔线
      if (idx < this.categories.length - 1) {
        Divider().color(COLOR_DIVIDER).margin({ left: PAGE_PADDING })
      }
    })
  }
  .backgroundColor(COLOR_CARD_BG)
  .borderRadius(CARD_RADIUS)
}

3.1 单行四元素布局

┌──────────────────────────────────────────────────┐
│ [■ 12vp] [文字日记 14vp] [12 MB 13vp] [26% 12vp] │
│                                                  │
│  12vp     layoutWeight(1)    auto          36vp  │
└──────────────────────────────────────────────────┘
   height: 50vp

3.2 色块设计

类别颜色含义
文字日记#F5A623 琥珀主类别
图片附件#C8956A 棕橙占用最大
其他#E8D5B0 浅米杂项

颜色饱和度与「占用大小」正相关——图片最大,颜色最深沉。

3.3 百分比对齐

Text(`${item.pct}%`)
  .width(36).textAlign(TextAlign.End)

固定 36vp 宽度 + 右对齐,确保「12%」「26%」「62%」数字右边缘对齐。


四、清理缓存按钮

@Builder
ClearCacheButtonBuilder() {
  Button('清理缓存')
    .width('100%').height(50)
    .fontSize(16).fontColor(COLOR_WHITE).fontWeight(FontWeight.Bold)
    .backgroundColor(COLOR_AMBER)
    .borderRadius(BTN_RADIUS_FULL)
    .onClick(() => this.confirmClear())
}

4.1 二次确认

清理缓存是「会改变状态」的操作,必须二次确认:

confirmClear() {
  AlertDialog.show({
    title: '清理缓存',
    message: '将清除临时文件,日记数据不受影响。是否继续?',
    buttons: [
      { text: '取消', role: ButtonRole.CANCEL },
      {
        text: '清理',
        role: ButtonRole.DESTRUCTIVE,
        action: () => this.doClear()
      }
    ]
  })
}

async doClear() {
  try {
    const freedBytes = await storageService.clearCache()
    const freedMB = (freedBytes / 1024 / 1024).toFixed(1)
    promptAction.showToast({ message: `已释放 ${freedMB} MB` })

    // 刷新分类数据
    await this.refreshCategories()
  } catch (e) {
    promptAction.showToast({ message: '清理失败:' + e.message })
  }
}

五、空间数据的获取

5.1 异步加载

async aboutToAppear() {
  await this.refreshCategories()
}

async refreshCategories() {
  try {
    const data = await storageService.getSpaceInfo()
    this.categories = data.categories
    this.totalSize = data.totalSize
    this.usedPct = data.usedPct
  } catch (e) {
    console.error('Failed to load space info', e)
  }
}

5.2 数据结构

interface CategoryItem {
  label: string       // 类别名称
  size: string        // 占用大小(格式化后的字符串)
  color: string       // 色块颜色
  pct: number         // 占总容量的百分比
}

六、与其他页面的协作关系

MinePage (我的)
    │ 点击「空间管理」
    ▼
SpaceManagePage (空间管理)
    │ 点击「清理缓存」
    ▼
storageService.clearCache()
    │ 释放空间
    ▼
刷新 categories + Toast

七、常见问题与踩坑记录

Q1:进度条宽度不准确?

原因usedPct 未取整或超过 100。
解决Math.min(100, Math.round(pct * 100) / 100)

Q2:清理缓存后空间没变?

原因:系统缓存清理有延迟。
解决:Toast 后延时 500ms 再刷新数据。

Q3:分类列表不滚动?

原因:列表被 Scroll 包裹,但高度溢出。
解决:用 List 替代 ForEach + Column,或确保 ColumnScroll 内。


八、总结

本文通过「海风日记」的空间管理页,深入讲解了:

  1. 总览进度条 —— Stack 叠加 + 10vp 高度 + 容量文字
  2. 空间分类列表 —— 色块 + 标签 + 大小 + 百分比四元素
  3. 清理缓存 —— 二次确认 + Toast 反馈
  4. 数据获取 —— 异步加载 + 错误处理

下一篇文章将进入 第十一系列:Premium 订阅页,敬请期待。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值