HarmonyOS ArkTS 实战:实现一个校园悬赏寻物失物招领应用

项目效果

本文使用 HarmonyOS 和 ArkTS 实现一个校园悬赏寻物失物招领应用。

应用可以发布失物招领和寻物启事,设置悬赏金额,在线联系,认领审核,并提供物品分类、搜索、消息通知、我的发布、成功案例等完整功能。

项目使用 DevEco Studio 开发,适配 API 23 及以上版本。

运行效果

校园悬赏寻物失物招领应用

功能介绍

  • 发布失物招领
  • 发布寻物启事
  • 设置悬赏金额
  • 物品分类筛选
  • 搜索物品
  • 在线联系
  • 认领申请
  • 审核确认
  • 我的发布
  • 成功案例
  • 消息通知
  • 信用积分

定义数据结构

interface LostItem {
  id: number;
  type: string; // 失物招领/寻物启事
  title: string;
  description: string;
  category: string;
  location: string;
  time: string;
  reward: number;
  contact: string;
  status: string; // 进行中/已找到/已认领
  publisher: string;
  images: string[];
}

interface Claim {
  id: number;
  itemId: number;
  claimer: string;
  description: string;
  time: string;
  status: string; // 待审核/已通过/已拒绝
}

初始化页面状态

@State private tabIndex: number = 0;
@State private currentType: string = '全部';
@State private currentCategory: string = '全部';

@State private types: string[] = ['全部', '失物招领', '寻物启事'];
@State private categories: string[] = ['全部', '证件卡片', '电子设备', '书籍文具', '钥匙雨伞', '其他'];

@State private items: LostItem[] = [
  { id: 1, type: '失物招领', title: '捡到一张校园卡', description: '在图书馆三楼捡到校园卡,名字张某某', category: '证件卡片', location: '图书馆三楼', time: '2026-07-22 10:00', reward: 0, contact: '微信:xxx', status: '进行中', publisher: '李同学', images: [] },
  { id: 2, type: '寻物启事', title: '寻找丢失的AirPods', description: '今天下午在食堂丢失白色AirPods,有重谢', category: '电子设备', location: '一食堂', time: '2026-07-22 12:30', reward: 50, contact: '电话:xxx', status: '进行中', publisher: '王同学', images: [] },
  { id: 3, type: '失物招领', title: '捡到一串钥匙', description: '在教学楼A楼门口捡到钥匙,有卡通挂件', category: '钥匙雨伞', location: '教学楼A楼', time: '2026-07-21 16:00', reward: 0, contact: 'QQ:xxx', status: '已认领', publisher: '赵同学', images: [] },
];

@State private claims: Claim[] = [];
@State private nextId: number = 10;

发布物品

private publishItem(type: string, title: string, description: string, category: string, location: string, reward: number, contact: string): void {
  const item: LostItem = {
    id: this.nextId, type, title, description, category, location,
    time: new Date().toLocaleString(), reward, contact,
    status: '进行中', publisher: '我', images: []
  };
  this.items = [item, ...this.items];
  this.nextId += 1;
}

申请认领

private applyClaim(itemId: number, description: string): void {
  const claim: Claim = {
    id: this.nextId, itemId, claimer: '我', description,
    time: new Date().toLocaleString(), status: '待审核'
  };
  this.claims = [claim, ...this.claims];
  this.nextId += 1;
}

确认找到/认领

private markAsResolved(itemId: number): void {
  this.items = this.items.map(i => i.id === itemId ? { ...i, status: i.type === '寻物启事' ? '已找到' : '已认领' } : i);
}

物品卡片组件

@Builder
ItemCard(item: LostItem) {
  Column({ space: 8 }) {
    Row() {
      Text(item.type)
        .fontSize(12)
        .fontColor(Color.White)
        .padding({ left: 8, right: 8, top: 4, bottom: 4 })
        .backgroundColor(item.type === '失物招领' ? '#10B981' : '#F59E0B')
        .borderRadius(10)
      if (item.reward > 0) {
        Text(`悬赏¥${item.reward}`)
          .fontSize(12)
          .fontColor('#EF4444')
          .fontWeight(FontWeight.Bold)
      }
      Blank()
      Text(item.status)
        .fontSize(12)
        .fontColor(item.status === '进行中' ? '#2563EB' : '#94A3B8')
    }
    .width('100%')
    
    Text(item.title)
      .fontSize(16)
      .fontWeight(FontWeight.Medium)
      .fontColor('#082F49')
      .width('100%')
    
    Text(item.description)
      .fontSize(13)
      .fontColor('#64748B')
      .width('100%')
      .maxLines(2)
      .textOverflow({ overflow: TextOverflow.Ellipsis })
    
    Row() {
      Text(`📍 ${item.location}`)
        .fontSize(12)
        .fontColor('#94A3B8')
      Text(`🕐 ${item.time.split(' ')[0]}`)
        .fontSize(12)
        .fontColor('#94A3B8')
      Blank()
      Text(item.publisher)
        .fontSize(12)
        .fontColor('#94A3B8')
    }
    .width('100%')
    .margin({ top: 4 })
    
    if (item.status === '进行中') {
      Button(item.type === '失物招领' ? '我要认领' : '我看到了')
        .width('100%')
        .height(36)
        .fontSize(14)
        .backgroundColor('#082F49')
        .margin({ top: 8 })
        .onClick(() => this.applyClaim(item.id, '描述物品特征'))
    }
  }
  .width('100%')
  .padding(16)
  .backgroundColor('#F0F9FF')
  .borderRadius(12)
}

页面布局

build() {
  Column() {
    // 搜索栏
    TextInput({ placeholder: '搜索丢失物品' })
      .width('100%')
      .height(40)
      .margin(20)
      .backgroundColor('#F0F9FF')
      .borderRadius(20)
    
    // 类型筛选
    Scroll(Axis.Horizontal) {
      Row({ space: 8 }) {
        ForEach(this.types, (t: string) => {
          Text(t)
            .fontSize(13)
            .fontColor(this.currentType === t ? Color.White : '#082F49')
            .padding({ left: 12, right: 12, top: 6, bottom: 6 })
            .backgroundColor(this.currentType === t ? '#082F49' : '#E0F2FE')
            .borderRadius(16)
            .onClick(() => this.currentType = t)
        })
      }
    }
    .scrollBar(BarState.Off)
    .width('100%')
    .padding({ left: 20, right: 20 })
    
    // 分类筛选
    Scroll(Axis.Horizontal) {
      Row({ space: 8 }) {
        ForEach(this.categories, (c: string) => {
          Text(c)
            .fontSize(12)
            .fontColor(this.currentCategory === c ? '#082F49' : '#64748B')
            .padding({ left: 10, right: 10, top: 5, bottom: 5 })
            .backgroundColor(this.currentCategory === c ? '#BAE6FD' : '#F0F9FF')
            .borderRadius(14)
            .onClick(() => this.currentCategory = c)
        })
      }
    }
    .scrollBar(BarState.Off)
    .width('100%')
    .padding({ left: 20, right: 20, top: 12 })
    
    // 物品列表
    List({ space: 12 }) {
      ForEach(this.items.filter(i => 
        (this.currentType === '全部' || i.type === this.currentType) &&
        (this.currentCategory === '全部' || i.category === this.currentCategory)
      ), (item: LostItem) => {
        ListItem() { this.ItemCard(item) }
      })
    }
    .width('100%')
    .padding(20)
    .layoutWeight(1)
    
    // 发布按钮
    Button('+ 发布')
      .width('90%')
      .height(48)
      .fontSize(16)
      .backgroundColor('#082F49')
      .borderRadius(24)
      .margin({ bottom: 20 })
      .onClick(() => this.publishItem('寻物启事', '物品名称', '详细描述', '其他', '地点', 0, '联系方式'))
  }
  .width('100%')
  .height('100%')
  .backgroundColor(Color.White)
}

页面设计说明

主题色采用sky-950#082F49,体现失物招领的信任、可靠。蓝色系给人安全感,绿色招领黄色寻物清晰区分,悬赏金额红色突出。

SDK配置

API 24,compatibleSdkVersion: “6.1.1(24)”

运行项目

将代码复制到 entry/src/main/ets/pages/Index.ets 即可运行。

项目总结

实现了失物招领和寻物启事发布、筛选、认领、悬赏、状态更新等功能。掌握了多条件筛选、卡片组件、状态标签、发布流程等。后续可加入图片上传、聊天功能、位置定位、信用系统、物品匹配、失物归还奖励等功能。

Logo

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

更多推荐