鸿蒙APP开发-带你走进旧物集的时间线与收藏管理

旧物集的物品故事:时间线与收藏管理

如果你喜欢收藏旧物,推荐去鸿蒙应用市场搜一下**「旧物集」**,下载体验体验。记录珍藏物品、写物品故事、按时间线浏览,一套走下来对每件珍藏都会有更完整的记录。体验完再回来看这篇文章,你会更清楚时间线浏览和收藏管理背后是怎么实现的。


写在前面

大家好,我是一名写了十多年Web前端的老兵。从jQuery时代一路走到React/Vue,CSS3动画、requestAnimationFrame、Web Animation API这些都算是看家本领。去年开始转战鸿蒙生态,用ArkTS开发App,这一路踩了不少坑,也积累了不少心得。

很多人觉得"前端转鸿蒙"应该很容易------都是写UI嘛,组件化、状态管理、生命周期,概念都差不多。但真正上手之后你会发现,相似的地方让你觉得亲切,不同的地方让你抓狂

比如:

  • 时间线渲染 :React的map()变成了ForEach,分组逻辑需要自己处理。
  • 数据存储 :Web的localStorage到了ArkTS变成了@ohos.data.preferences
  • 收藏交互 :Web的点击事件变成了鸿蒙的onClick

接下来这篇文章,我会用"旧物集"的实际开发经历,带你看看物品时间线、收藏管理、数据统计的实现。


这篇文章聊什么

旧物集的物品故事功能,核心要解决三个问题:

  1. 时间线:按年份浏览珍藏物品
  2. 收藏管理:收藏重要物品
  3. 数据统计:分析收藏数据

第一步:时间线页面

typescript 复制代码
@Entry
@Component
struct TimelinePage {
  @State items: CollectibleItem[] = []
  @State groupedByYear: Record<string, CollectibleItem[]> = {}

  async aboutToAppear() {
    await this.loadItems()
    this.groupItems()
  }

  async loadItems() {
    const store = await preferences.getPreferences(getContext(), 'jiuwuji_data');
    const stored = await store.get('items', '[]') as string;
    this.items = JSON.parse(stored);
    this.items.sort((a, b) =>
      new Date(b.acquiredDate).getTime() - new Date(a.acquiredDate).getTime()
    );
  }

  groupItems() {
    this.groupedByYear = {};
    this.items.forEach(item => {
      const year = new Date(item.acquiredDate).getFullYear().toString();
      if (!this.groupedByYear[year]) {
        this.groupedByYear[year] = [];
      }
      this.groupedByYear[year].push(item);
    });
  }

  build() {
    Column() {
      Text('物品时间线')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 16 })

      List({ space: 16 }) {
        ForEach(Object.entries(this.groupedByYear), ([year, yearItems]) => {
          ListItem() {
            Column() {
              Text(year)
                .fontSize(18)
                .fontWeight(FontWeight.Bold)
                .fontColor('#F59E0B')
                .margin({ bottom: 12 })

              ForEach(yearItems, (item: CollectibleItem) => {
                Row() {
                  Column() {
                    Circle({ width: 12, height: 12 })
                      .fill('#F59E0B')
                    Row()
                      .width(2)
                      .height(80)
                      .backgroundColor('#374151')
                  }
                  .margin({ right: 12 })

                  Column() {
                    Row() {
                      Text(this.getCategoryIcon(item.category))
                        .fontSize(20)
                      Text(item.name)
                        .fontSize(16)
                        .fontWeight(FontWeight.Bold)
                        .layoutWeight(1)
                        .margin({ left: 8 })
                    }

                    Text(item.story)
                      .fontSize(14)
                      .fontColor('#D1D5DB')
                      .margin({ top: 8 })
                      .maxLines(3)
                      .textOverflow({ overflow: TextOverflow.Ellipsis })
                  }
                  .layoutWeight(1)
                  .padding(12)
                  .backgroundColor('#1F2937')
                  .borderRadius(12)
                }
                .margin({ bottom: 12 })
              })
            }
          }
        })
      }
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
    .padding(16)
    .backgroundColor('#111827')
  }

  private getCategoryIcon(category: string): string {
    return ITEM_CATEGORIES.find(c => c.id === category)?.icon || '🎁';
  }
}

第二步:收藏管理

typescript 复制代码
@Entry
@Component
struct FavoritesPage {
  @State favorites: CollectibleItem[] = []

  async aboutToAppear() {
    const store = await preferences.getPreferences(getContext(), 'jiuwuji_data');
    const stored = await store.get('items', '[]') as string;
    const items: CollectibleItem[] = JSON.parse(stored);
    this.favorites = items.filter(i => i.isFavorite);
  }

  build() {
    Column() {
      Text('珍藏物品')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 16 })

      if (this.favorites.length === 0) {
        Text('还没有珍藏的物品')
          .fontSize(16)
          .fontColor('#9CA3AF')
      } else {
        List({ space: 12 }) {
          ForEach(this.favorites, (item: CollectibleItem) => {
            ListItem() {
              Row() {
                if (item.photoUri) {
                  Image(item.photoUri)
                    .width(60)
                    .height(60)
                    .objectFit(ImageFit.Cover)
                    .borderRadius(8)
                }
                Column() {
                  Text(item.name)
                    .fontSize(16)
                    .fontWeight(FontWeight.Bold)
                  Text(this.getCategoryName(item.category))
                    .fontSize(13)
                    .fontColor('#9CA3AF')
                    .margin({ top: 4 })
                }
                .layoutWeight(1)
                .margin({ left: 12 })
              }
              .width('100%')
              .padding(12)
              .backgroundColor('#1F2937')
              .borderRadius(12)
            }
          })
        }
        .layoutWeight(1)
      }
    }
    .width('100%')
    .height('100%')
    .padding(16)
    .backgroundColor('#111827')
  }

  private getCategoryName(category: string): string {
    return ITEM_CATEGORIES.find(c => c.id === category)?.name || category;
  }
}

第三步:数据统计

typescript 复制代码
interface ItemStats {
  totalItems: number;
  favoriteCount: number;
  categoryDistribution: Record<string, number>;
  statusDistribution: Record<string, number>;
}

总结

这篇文章围绕"旧物集"的物品故事功能,讲解了三个核心主题:

  1. 时间线:按年份分组的物品时间线
  2. 收藏管理:珍藏物品的筛选和展示
  3. 数据统计:物品分类和状态的统计分析

时间线的核心是数据分组------用Record<string, CollectibleItem[]>按年份分组。


如果你喜欢收藏旧物,希望这篇文章能帮你理解旧物集背后的管理逻辑。去鸿蒙应用市场下载体验一下吧,有问题欢迎交流。

相关推荐
尼斯湖皮皮怪1 小时前
iceCoder双模详解
javascript
拂尘子1 小时前
前端屎山代码救星:这个 MCP 把 7000 行页面压成 60 行骨架,Token 直接省掉 90%+
前端·ai编程·mcp
小雨下雨的雨1 小时前
月相分析工具鸿蒙PC Electron框架技术实现详解
前端·javascript·华为·electron
布依前端2 小时前
基于 Vue 3 的 Tiptap 富文本编辑器实践:tiptap-editor-vue3 项目介绍
前端·javascript·vue.js
阿狸猿2 小时前
论负载均衡技术在 Web 系统中的应用
运维·前端·负载均衡
橘猫走江湖2 小时前
Cursor Vibe Coding 开发指南
前端
因_崔斯汀2 小时前
网页为什么需要框架?
前端
前端 贾公子2 小时前
Tailwind CSS `shrink-0`是啥意思?
前端
奥利奥夹心脆芙2 小时前
OTel / Logstash / Fluentd 全维对比,及统一日志与指标管道的 AWS ECS 落地
javascript