> 技术文档 > HarmonyOS从入门到精通:自定义组件开发指南(十一):实战案例 —— 电商应用商品卡片组件_推荐系统之商品标签tag设计

HarmonyOS从入门到精通:自定义组件开发指南(十一):实战案例 —— 电商应用商品卡片组件_推荐系统之商品标签tag设计


HarmonyOS从入门到精通:自定义组件开发指南(十一):实战案例 —— 电商应用商品卡片组件

在电商应用中,商品卡片是连接用户与商品的核心交互载体,其设计质量直接影响用户浏览体验和购买转化。一个优秀的商品卡片需兼顾信息展示的完整性、交互的流畅性和视觉的吸引力。本文将通过实战案例,详解如何基于HarmonyOS自定义组件技术,开发一个功能完整、可复用、高性能的电商商品卡片组件,整合组件复合、事件回调、布局优化等核心技术,为电商应用开发提供可直接复用的解决方案。

一、案例需求与技术选型

1. 核心功能需求

电商场景下的商品卡片需精准传递商品价值并引导用户决策,核心要素包括:

  • 视觉核心:商品主图展示,占据卡片显著位置;
  • 信息分层:标签(如“新品”)、标题(核心描述)、价格(现价/原价对比)、评分与销量(社会证明);
  • 交互闭环:点击卡片进入详情页、点击按钮直接加入购物车,两种交互需互不干扰。

2. 技术栈选型

为实现上述需求,本案例整合HarmonyOS自定义组件开发的多项核心技术,形成完整技术体系:

  • 组件复合模式:通过基础组件(评分、标签)组合构建高内聚的商品卡片;
  • TypeScript接口:定义商品数据结构规范,确保类型安全与数据一致性;
  • 自定义事件机制:设计onCardClickonAddToCart回调,实现组件与业务逻辑解耦;
  • 容器布局优化:使用Stack(图片叠标签)、Grid(列表布局)等组件实现响应式设计;
  • 用户体验细节:文本省略、事件冒泡阻止、条件渲染等细节处理,提升交互质感。

二、核心代码实现与解析

1. 数据模型定义:Product接口

通过TypeScript接口规范商品数据结构,明确字段含义与可选性,为组件开发提供清晰的数据契约:

// 商品数据接口:规范商品卡片所需的所有字段interface Product { id: string;  // 商品唯一标识(用于交互回调) title: string; // 商品名称(必填) price: number; // 现价(必填,保留两位小数) originalPrice?: number; // 原价(可选,用于折扣展示) imageUrl: string; // 商品主图URL(支持本地/网络资源) tags?: string[]; // 商品标签(如“新品”“热销”,可选) rating: number;  // 商品评分(1-5分,支持小数) salesCount: number; // 销量(用于展示“xx人已购买”)}

设计要点

  • 字段分类:区分必填字段(idtitle等)与可选字段(originalPricetags),适配不同商品类型(如促销商品与常规商品);
  • 类型约束:评分与销量使用number类型,支持数值计算(如评分排序、销量格式化);
  • 扩展性预留:字段设计兼顾未来扩展(如新增discount字段支持折扣百分比展示)。

2. 基础组件开发:原子化复用

(1)ProductRating:商品评分组件

专注于星级评分展示,通过动态渲染实现满星/空星切换,是商品卡片的核心决策参考元素:

// 评分组件:展示星级评分与具体分数@Componentstruct ProductRating { // 外部传入的评分值(1-5分) rating: number = 0; build() { Row({ space: 4 }) { // 星星与分数间距4px,保持紧凑 // 循环渲染5颗星星,根据评分动态切换状态 ForEach(new Array(5).fill(0), (_, index) => { Image(index < Math.floor(this.rating)  ? $r(\'app.media.ic_star_filled\') // 满星图标(评分≥当前索引) : $r(\'app.media.ic_star_empty\') // 空星图标(评分<当前索引) ) .width(12) .height(12) // 小巧尺寸,避免抢占视觉焦点 }) // 展示具体评分值(保留1位小数) Text(`${this.rating.toFixed(1)}`) .fontSize(12) .fontColor(\'#FF9500\') // 橙色系,与星星视觉关联 } }}

设计亮点

  • 动态渲染逻辑:通过index < Math.floor(this.rating)判断星星状态,支持半星评分(如需展示半星,可新增半星图标并通过rating % 1 >= 0.5判断);
  • 视觉一致性:星星尺寸与文字大小严格匹配(12px),颜色统一为橙色系,强化品牌识别;
  • 高复用性:可直接用于评论列表、商品详情等场景,保持评分展示风格一致。
(2)ProductTag:商品标签组件

统一标签样式,突出商品特性(如“新品”“限时优惠”),通过视觉对比吸引用户注意:

// 标签组件:展示商品标签(如“新品”“热销”)@Componentstruct ProductTag { // 标签文本内容 text: string = \'\'; build() { Text(this.text) .fontSize(10) // 小字体设计,避免喧宾夺主 .backgroundColor(\'#FFF0E6\') // 浅橙色背景,柔和不刺眼 .fontColor(\'#FF6E1B\') // 橙色文字,与背景形成适度对比 .padding({ left: 6, right: 6, top: 2, bottom: 2 }) // 紧凑内边距,节省空间 .borderRadius(4) // 圆角设计,弱化标签的生硬感 }}

设计亮点

  • 样式封装:固定背景色、文字色、圆角等样式属性,确保所有标签视觉统一;
  • 尺寸优化:10px字体+最小内边距设计,在传递信息的同时避免占用过多空间;
  • 场景适配:可直接复用至订单状态、分类筛选等场景,保持应用风格一致性。

3. 复合组件开发:ProductCard商品卡片

整合基础组件与业务元素,构建完整的商品卡片,实现信息展示与交互功能的有机结合:

// 主商品卡片组件:整合图片、标签、信息、按钮@Componentexport struct ProductCard { // 商品数据(通过Product接口约束,确保类型安全) product: Product = { id: \'\', title: \'\', price: 0, imageUrl: \'\', rating: 0, salesCount: 0 }; // 自定义事件:卡片点击(用于跳转详情页) onCardClick?: (productId: string) => void; // 自定义事件:加入购物车按钮点击 onAddToCart?: (productId: string) => void; build() { Column() { // 卡片整体采用垂直布局,分为图片区与信息区 // 第一部分:商品图片+标签(Stack实现图片叠标签) Stack({ alignContent: Alignment.BottomStart }) { // 标签居左下对齐 // 商品主图 Image(this.product.imageUrl) .width(\'100%\') .height(180) // 固定高度,确保列表中卡片整齐排列 .objectFit(ImageFit.Cover) // 裁剪适配,保证图片不变形 .borderRadius({ topLeft: 8, topRight: 8 }) // 顶部圆角,与卡片风格统一 // 商品标签(最多显示2个,避免视觉拥挤) if (this.product.tags && this.product.tags.length > 0) { Row({ space: 4 }) // 标签间距4px .padding(8) // 距离图片边缘8px,保持呼吸感 { ForEach(this.product.tags.slice(0, 2), (tag: string) => {  ProductTag({ text: tag }) // 复用标签组件 }) } } } .width(\'100%\') // 第二部分:商品信息区(标题、价格、评分、按钮) Column({ space: 6 }) { // 信息项垂直间距6px,保持紧凑 // 商品标题(最多2行,超出省略) Text(this.product.title) .fontSize(14) .fontWeight(FontWeight.Medium) .maxLines(2) // 限制2行,避免标题过长导致卡片高度不一 .textOverflow({ overflow: TextOverflow.Ellipsis }) // 超出显示省略号,保持整洁 // 价格信息(现价+原价对比) Row({ space: 4 }) { // 现价(突出展示,刺激购买) Text(`¥${this.product.price.toFixed(2)}`) .fontSize(16) .fontColor(\'#FF6E1B\') // 主题色,强化价格感知 .fontWeight(FontWeight.Bold)  // 原价(划线显示,仅当原价>现价时展示,突出折扣) if (this.product.originalPrice && this.product.originalPrice > this.product.price) { Text(`¥${this.product.originalPrice.toFixed(2)}`)  .fontSize(12)  .fontColor(\'#999999\') // 灰色,弱化展示  .decoration({ type: TextDecorationType.LineThrough }) // 划线效果,强化折扣感知 } } // 评分与销量(社会证明,辅助决策) Row() { ProductRating({ rating: this.product.rating }) // 复用评分组件  Text(`${this.product.salesCount}人已购买`) .fontSize(12) .fontColor(\'#999999\') // 灰色,次要信息 .margin({ left: 10 }) // 与评分保持10px间距,避免拥挤 } // 加入购物车按钮(核心转化入口) Button(\'加入购物车\') .width(\'100%\') .height(32) // 矮按钮设计,节省空间 .fontSize(12) .fontWeight(FontWeight.Medium) .backgroundColor(\'#FF6E1B\') // 主题色,突出交互入口 .borderRadius(16) // 圆角按钮,视觉柔和 .margin({ top: 8 }) // 与上方信息保持8px间距 .onClick((event) => { event.stopPropagation(); // 阻止事件冒泡,避免触发卡片点击 if (this.onAddToCart) {  this.onAddToCart(this.product.id); // 传递商品ID,便于后续业务处理 } }) } .width(\'100%\') .padding(10) // 信息区内边距10px,避免内容贴边 } .width(\'100%\') .backgroundColor(Color.White) .borderRadius(8) // 卡片圆角8px,柔和视觉 .shadow({ radius: 4, color: \'#33000000\', offsetX: 0, offsetY: 2 }) // 轻微阴影,增强层次感 .onClick(() => { if (this.onCardClick) { this.onCardClick(this.product.id); // 卡片点击传递商品ID,用于跳转详情 } }) }}

核心技术解析

  • 布局架构

    • 上下分区:图片区(带标签)与信息区严格分离,结构清晰;
    • 响应式设计:宽度100%自适应父容器,可无缝适配两列/三列列表布局;
    • 视觉层次:通过阴影(shadow)、圆角(borderRadius)与背景色对比,强化卡片立体感。
  • 交互设计

    • 事件隔离:加入购物车按钮点击通过event.stopPropagation()阻止冒泡,确保与卡片点击(进入详情)互不干扰;
    • 回调机制:通过onCardClickonAddToCart暴露交互接口,父组件可灵活绑定业务逻辑(如路由跳转、购物车API调用),实现组件与业务解耦。
  • 体验优化

    • 文本控制:标题最多2行+省略号,避免信息溢出破坏布局;
    • 条件渲染:原价仅在originalPrice > price时展示,标签最多显示2个,避免界面拥挤;
    • 视觉统一:价格色(#FF6E1B)、按钮色与标签色形成视觉闭环,强化品牌识别。

4. 页面集成:商品列表展示

ProductListPage中批量使用ProductCard组件,构建完整的商品列表页面,验证组件的复用性与布局效果:

// 商品列表页面:使用ProductCard组件展示商品列表@Entry@Componentstruct ProductListPage { // 商品数据源(模拟数据,实际可从API获取) @State products: Product[] = [ { id: \'1\', title: \'鸿蒙智能手表 GT3 Pro 钛金属表带\', price: 2488, originalPrice: 2699, // 原价高于现价,显示划线 imageUrl: \'/assets/products/watch.jpg\', tags: [\'新品\', \'限时优惠\'], // 显示2个标签 rating: 4.9, salesCount: 3205 }, { id: \'2\', title: \'鸿蒙智能手机 P50 Pro 8GB+256GB 曜金黑\', price: 5988, originalPrice: 6488, imageUrl: \'/assets/products/phone.jpg\', tags: [\'热销\'], // 显示1个标签 rating: 4.8, salesCount: 15230 }, { id: \'3\', title: \'鸿蒙平板电脑 MatePad Pro 10.8英寸 8GB+256GB\', price: 3799, imageUrl: \'/assets/products/tablet.jpg\', // 无原价,不显示划线 tags: [\'教育优惠\'], rating: 4.7, salesCount: 2158 } ]; build() { Column() { // 页面标题 Text(\'商品列表\') .fontSize(24) .fontWeight(FontWeight.Bold) .margin({ top: 20, bottom: 16 }) // 商品网格(两列布局) Grid() { ForEach(this.products, (product: Product) => { GridItem() { ProductCard({  product: product,  onCardClick: (productId) => { console.info(`点击商品ID: ${productId},准备跳转详情页`); // 实际场景可调用路由API跳转详情页  },  onAddToCart: (productId) => { console.info(`商品ID: ${productId} 已添加到购物车`); // 实际场景可调用购物车API添加商品  } }) } }) } .columnsTemplate(\'1fr 1fr\') // 两列布局,平均分配宽度 .columnsGap(12) // 列间距12px .rowsGap(12) // 行间距12px .width(\'100%\') } .width(\'100%\') .height(\'100%\') .backgroundColor(\'#F5F5F5\') // 浅灰色背景,突出白色卡片 .padding(16) // 页面内边距16px,避免卡片贴边 }}

页面设计亮点

  • 网格布局:使用Grid组件的columnsTemplate(\'1fr 1fr\')实现两列布局,充分利用手机屏幕空间;
  • 间距体系:列间距(columnsGap)与行间距(rowsGap)均为12px,确保卡片间呼吸感适中;
  • 数据驱动:通过ForEach循环渲染商品卡片,数据与UI严格分离,便于后续支持分页加载、筛选排序等功能。

三、组件扩展与优化方向

本案例实现了商品卡片的基础功能,实际开发中可根据业务需求扩展以下能力:

  1. 图片加载优化

    • 懒加载:结合IntersectionObserver实现图片懒加载,仅加载可视区域内的图片,提升列表初始化速度;
    • 占位与错误处理:图片加载完成前显示骨架屏,加载失败时显示默认图,避免空白闪烁。
  2. 交互体验增强

    • 悬停反馈:添加onHover事件,鼠标悬停时卡片轻微放大(1.02倍),增强交互感知;
    • 按钮动效:加入购物车按钮点击时添加缩放动画(0.95倍→1倍),提升操作反馈质感。
  3. 功能扩展性

    • 收藏功能:在卡片右上角添加收藏图标,通过@Prop绑定选中状态,支持一键收藏;
    • 规格选择:通过插槽(@BuilderParam)添加规格选择器(如颜色、尺寸),适配多规格商品;
    • 库存状态:低库存时显示“仅剩x件”提示,刺激转化;无库存时按钮置灰并显示“缺货”。
  4. 性能优化

    • 列表懒渲染:使用LazyForEach替代ForEach,大数据量下仅渲染可视区域卡片,降低内存占用;
    • 图片缓存:通过ImageCache缓存已加载图片,避免重复请求,提升二次加载速度。

四、总结

本实战案例基于HarmonyOS自定义组件技术,构建了一个功能完整、可复用的电商商品卡片组件,展示了组件化开发的核心方法论:

  • 分层设计:基础组件(评分、标签)专注单一功能,复合组件(商品卡片)负责整合与交互,实现“高内聚、低耦合”;
  • 接口驱动:通过TypeScript接口规范数据结构,确保类型安全与数据一致性,减少运行时错误;
  • 交互解耦:基于自定义事件回调实现组件与业务逻辑分离,提升组件复用性;
  • 细节致胜:文本控制、条件渲染、事件隔离等细节处理,直接影响用户体验与转化效率。

掌握这些技术,开发者可快速构建电商应用所需的其他核心组件(如购物车、订单列表),形成完整的组件库,显著提升开发效率与应用质量。在实际开发中,需结合业务场景平衡功能丰富度与性能,打造既美观又高效的电商体验。