HarmonyOS ArkTS 实战:从零实现一个毕业季跳蚤市场应用
项目背景
每年毕业季,校园里都会掀起一股“闲置流转”热潮:即将离校的学长学姐们有大量教材、电子产品、生活用品需要处理,而学弟学妹们则希望能以实惠的价格淘到好东西。然而,传统的方式——贴吧发帖、微信群刷屏、打印传单张贴——不仅效率低下,还存在信息杂乱、交易无保障等问题。
本文就带大家使用 HarmonyOS 和 ArkTS 框架,从零搭建一个「毕业季跳蚤市场」应用。该应用涵盖商品发布、分类浏览、在线沟通、下单交易等核心功能,同时提供分类筛选、搜索、收藏、我的发布、求购专区、毕业专区等完整模块。项目使用 DevEco Studio 开发,适配 API 23 及以上版本。
读完本文,你将掌握 Grid 网格布局、组件状态管理、状态流转控制、卡片阴影等实用技巧,并能在此基础上扩展出完整的校园二手交易平台。
项目效果
启动应用后,首页以清新的深青色调呈现,顶部展示毕业季主题标语,下方是横向滚动的分类标签和双列商品网格。每张商品卡片包含缩略图区域、毕业清仓标签、价格信息和收藏按钮,点击可直接下单购买。
运行效果

应用整体风格简洁明快,商品信息一目了然,操作路径短、反馈及时,非常适合校园场景下的快速交易。
功能介绍
本应用围绕二手交易的核心流程,设计了以下功能模块:
- 商品发布:用户可发布闲置物品,填写标题、价格、分类、成色、描述等信息。
- 分类浏览:按教材书籍、电子数码、生活用品、服饰鞋包、运动器材等类别筛选商品。
- 商品搜索:通过关键词快速定位目标商品。
- 价格筛选:按照价格区间过滤,方便比价。
- 商品收藏:收藏感兴趣的商品,便于后续集中查看和对比。
- 在线沟通:买卖双方可通过内置聊天功能沟通细节(后续扩展)。
- 下单购买:一键下单,商品状态自动从“在售”变更为“已预定”。
- 我的发布:查看自己发布的商品列表,管理在售/已售状态。
- 我的订单:查看自己作为买家或卖家的订单记录。
- 求购专区:发布求购信息,反向匹配闲置商品。
- 毕业专区:筛选带有“毕业清仓”标签的商品,集中浏览。
- 当面交易:交易确认后面对面交付,无需物流。
定义数据结构
良好的数据结构是应用稳定运行的基石。我们首先定义 Goods 和 Order 两个核心接口:
interface Goods {
id: number;
title: string;
price: number;
originalPrice: number;
category: string;
condition: string; // 全新/几乎全新/轻微使用/有使用痕迹
seller: string;
location: string;
images: string[];
description: string;
status: string; // 在售/已预定/已售出
publishTime: string;
isGraduation: boolean;
}
interface Order {
id: number;
goodsId: number;
goodsTitle: string;
price: number;
buyer: string;
seller: string;
status: string; // 待见面/已完成/已取消
createTime: string;
}
Goods 接口涵盖了商品的完整信息,其中 condition 和 isGraduation 字段分别用于标识新旧程度和是否为毕业清仓商品,status 字段贯穿整个交易流程的三种状态。Order 接口则记录了每次交易的买卖双方、金额、状态和时间。
初始化页面状态
利用 ArkTS 的 @State 装饰器,我们可以轻松管理页面的响应式数据:
@State private tabIndex: number = 0;
@State private currentCategory: string = '全部';
@State private searchKeyword: string = '';
@State private categories: string[] = ['全部', '教材书籍', '电子数码', '生活用品', '服饰鞋包', '运动器材', '其他'];
@State private goods: Goods[] = [
{ id: 1, title: '九成新高等数学教材', price: 10, originalPrice: 45, category: '教材书籍', condition: '几乎全新', seller: '张同学', location: '1号楼', images: [], description: '高数第七版上册,几乎全新,有少量笔记', status: '在售', publishTime: '2026-07-20', isGraduation: true },
{ id: 2, title: '自用iPad 2021 64G', price: 1800, originalPrice: 2999, category: '电子数码', condition: '轻微使用', seller: '李同学', location: '3号楼', images: [], description: '毕业出,无拆无修,带充电器', status: '在售', publishTime: '2026-07-21', isGraduation: true },
{ id: 3, title: '床上小桌子', price: 15, originalPrice: 39, category: '生活用品', condition: '有使用痕迹', seller: '王同学', location: '5号楼', images: [], description: '毕业清仓,自提', status: '在售', publishTime: '2026-07-22', isGraduation: true },
{ id: 4, title: '羽毛球拍尤尼克斯', price: 80, originalPrice: 200, category: '运动器材', condition: '轻微使用', seller: '赵同学', location: '体育馆', images: [], description: '入门级球拍,送球', status: '已预定', publishTime: '2026-07-19', isGraduation: false },
];
@State private orders: Order[] = [];
@State private favorites: number[] = [];
@State private nextId: number = 10;
这里初始化了分类列表、示例商品数据、订单数组、收藏数组以及自增 ID 计数器。示例数据涵盖了教材、数码、生活用品和运动器材等典型品类,既有毕业清仓商品也有普通闲置,方便演示不同状态的展示效果。
收藏商品
收藏功能使用数组存储已收藏的商品 ID,点击爱心图标时切换收藏状态。利用展开运算符创建新数组引用,触发 UI 响应式更新:
private toggleFavorite(goodsId: number): void {
if (this.favorites.includes(goodsId)) {
this.favorites = this.favorites.filter(id => id !== goodsId);
} else {
this.favorites = [...this.favorites, goodsId];
}
}
购买商品
下单逻辑包含以下几个步骤:校验商品是否存在且为“在售”状态、创建订单、更新商品状态为“已预定”、递增订单 ID。整个过程通过不可变数据更新确保 UI 正确刷新:
private buyGoods(goodsId: number): void {
const goods = this.goods.find(g => g.id === goodsId);
if (!goods || goods.status !== '在售') return;
const order: Order = {
id: this.nextId, goodsId, goodsTitle: goods.title, price: goods.price,
buyer: '我', seller: goods.seller, status: '待见面',
createTime: new Date().toLocaleDateString()
};
this.orders = [order, ...this.orders];
this.goods = this.goods.map(g => g.id === goodsId ? { ...g, status: '已预定' } : g);
this.nextId += 1;
}
确认交易
当买卖双方面对面完成交付后,调用 confirmDeal 将订单状态更新为“已完成”,同时将对应商品标记为“已售出”:
private confirmDeal(orderId: number): void {
const order = this.orders.find(o => o.id === orderId);
if (!order) return;
this.orders = this.orders.map(o => o.id === orderId ? { ...o, status: '已完成' } : o);
this.goods = this.goods.map(g => g.id === order.goodsId ? { ...g, status: '已售出' } : g);
}
至此,商品完整的状态流转路径为:在售 → 已预定 → 已售出,每一步对应的订单状态也同步变化,逻辑清晰、易于维护。
商品卡片组件
GoodsCard 是使用 @Builder 装饰器定义的自定义构建函数,负责渲染单张商品卡片。卡片采用白色圆角容器,顶部区域展示默认背景和状态标签,下方展示标题、价格和收藏按钮:
@Builder
GoodsCard(goods: Goods) {
Column({ space: 8 }) {
Stack() {
Column()
.width('100%')
.height(120)
.backgroundColor('#F0FDFA')
.borderRadius({ topLeft: 12, topRight: 12 })
if (goods.isGraduation) {
Text('毕业清仓')
.fontSize(10)
.fontColor(Color.White)
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.backgroundColor('#134E4A')
.borderRadius(8)
.position({ x: 8, y: 8 })
}
Text(goods.status === '在售' ? '' : goods.status)
.fontSize(11)
.fontColor(Color.White)
.padding({ left: 8, right: 8, top: 3, bottom: 3 })
.backgroundColor(goods.status === '已售出' ? '#94A3B8' : '#F59E0B')
.borderRadius(10)
.position({ x: '70%', y: 8 })
}
.width('100%')
Text(goods.title)
.fontSize(13)
.fontColor('#134E4A')
.width('100%')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.padding({ left: 8, right: 8 })
Row() {
Text(`¥${goods.price}`)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#0F766E')
Text(`¥${goods.originalPrice}`)
.fontSize(11)
.fontColor('#94A3B8')
.decoration({ type: TextDecorationType.LineThrough })
Blank()
Text('❤️')
.fontSize(16)
.fontColor(this.favorites.includes(goods.id) ? '#EF4444' : '#CBD5E1')
.onClick(() => this.toggleFavorite(goods.id))
}
.width('100%')
.padding({ left: 8, right: 8, bottom: 8 })
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({ radius: 4, color: '#134E4A10', offsetY: 2 })
.onClick(() => {
if (goods.status === '在售') this.buyGoods(goods.id);
})
}
卡片设计注重信息层级:毕业清仓标签固定在左上角,状态标签固定在右上角,标题支持最多两行显示并自动省略,原价使用删除线效果形成价格对比,收藏按钮根据状态切换红色/灰色。点击卡片时,在售商品可直接触发购买。
页面布局
build() 方法将上述组件组装为完整页面,从上到下依次为:顶部标题、搜索框、横向分类导航、商品网格和底部发布按钮:
build() {
Column() {
// 顶部标题
Text('🎓 毕业季跳蚤市场')
.fontSize(22)
.fontWeight(FontWeight.Bold)
.fontColor('#134E4A')
.width('100%')
.padding(20)
// 搜索
TextInput({ placeholder: '搜索闲置物品' })
.width('90%')
.height(40)
.backgroundColor('#F0FDFA')
.borderRadius(20)
.margin({ bottom: 12 })
// 分类
Scroll(Axis.Horizontal) {
Row({ space: 8 }) {
ForEach(this.categories, (c: string) => {
Text(c)
.fontSize(13)
.fontColor(this.currentCategory === c ? Color.White : '#134E4A')
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.backgroundColor(this.currentCategory === c ? '#134E4A' : '#CCFBF1')
.borderRadius(16)
.onClick(() => this.currentCategory = c)
})
}
}
.scrollBar(BarState.Off)
.width('100%')
.padding({ left: 20, right: 20 })
// 商品网格
Grid() {
ForEach(this.goods.filter(g =>
(this.currentCategory === '全部' || g.category === this.currentCategory) &&
g.status !== '已售出'
), (g: Goods) => {
GridItem() { this.GoodsCard(g) }
})
}
.columnsTemplate('1fr 1fr')
.columnsGap(12)
.rowsGap(12)
.width('100%')
.padding(20)
.layoutWeight(1)
// 发布按钮
Button('+ 发布闲置')
.width('90%')
.height(48)
.fontSize(16)
.backgroundColor('#134E4A')
.borderRadius(24)
.margin({ bottom: 20 })
}
.width('100%')
.height('100%')
.backgroundColor('#F8FAFC')
}
页面使用 layoutWeight(1) 让商品网格区域自动填充剩余空间,分类标签通过 Scroll 组件支持横向滚动,适应不同数量的分类项。暗纹浅灰背景 #F8FAFC 衬托白色卡片,整体视觉效果清爽、层次分明。
页面设计说明
主题色采用 teal-900 #134E4A,深青色既传递了校园的清新与活力,又保持了足够的可读性。毕业清仓标签使用同色系小圆角徽章,与主题呼应;价格使用加粗深绿,原价添加删除线形成视觉对比;收藏爱心在收藏/未收藏之间切换红色和浅灰,交互反馈直观。两列网格配合 12px 间距和圆角阴影卡片,在保持信息密度的同时让页面呼吸感十足。
SDK 配置
本项目基于 API 24 开发,兼容版本配置如下:
compatibleSdkVersion: "6.1.1(24)"
如需适配低版本设备,可将 API 版本下调至 23,代码无需额外修改。
运行项目
将完整代码复制到 entry/src/main/ets/pages/Index.ets 文件中,在 DevEco Studio 中连接真机或启动模拟器,点击运行即可体验。首次运行时会展示预设的示例商品数据,你可以通过修改 goods 数组来添加自己的测试数据。
项目总结
本文从数据结构设计入手,逐步实现了商品浏览、分类筛选、收藏切换、下单购买、交易确认等二手交易核心功能。在实现过程中,我们掌握了以下技术要点:
- Grid 网格布局:通过
columnsTemplate('1fr 1fr')实现双列自适应网格。 - 状态管理:利用
@State装饰器配合不可变数据更新,确保 UI 响应及时。 - 商品状态流转:设计“在售 → 已预定 → 已售出”的状态机,逻辑清晰可扩展。
- 自定义组件:使用
@Builder封装可复用的商品卡片,提升代码组织性。 - 卡片阴影与圆角:营造轻量化的视觉层次。
后续可以考虑扩展的功能方向包括:接入实时聊天服务实现买卖双方在线沟通、集成图片上传让商品展示更直观、添加求购发布与智能匹配、引入地址选择与校内定位、完善评价体系建立信用机制等。掌握了本文的基础架构后,这些扩展都可以在现有代码上平滑叠加,推荐读者动手尝试,打造属于自己的校园交易平台!
更多推荐


所有评论(0)