HarmonyOS6 半年磨一剑:RcTag 组件实战案例(一)内容展示与商品筛选

HarmonyOS6 半年磨一剑:RcTag 组件实战案例(一)内容展示与商品筛选

文章目录

Hello 各位开发者们大家好,我是若城,本篇是 RcTag 实战系列的第一篇文章,将通过两个真实业务场景,展示如何用 RcTag 构建内容详情页标签区域和电商商品分类筛选功能。


一、场景一:内容详情页标签展示

1.1 场景描述

文章详情页顶部展示文章分类标签、内容标签和难度标签,纯展示用途,无交互。这类需求在技术博客、内容社区、资讯类 App 中极为普遍,标签本身承载的是信息分类职责,设计上需要做到层次分明而不杂乱。

1.2 设计思路

这个场景的核心是在有限的屏幕宽度内,用视觉差异区分出三种信息层级:主分类(主语意最强)、难度(次要信息)、内容关键词(辅助信息)。

主分类标签采用实色填充,这是最高强调等级,抢先抓住用户视线。难度标签采用胶囊形状,缩小尺寸至 mini,既保持可读性又不抢占主分类的主导地位。内容关键词数量最多,若同样使用实色会造成视觉噪声,因此采用 rcTagPlain: true + rcTagPlainFill: true 的镂空填充风格——有背景色但背景透明度低,整体轻量,适合数量多的场景。

布局方面,第一行使用 Row 横向排列分类标签和难度标签,这两个标签数量固定,不会换行。第二行使用 Flex + FlexWrap.Wrap 展示内容关键词,标签数量不确定,自动换行是必要的。

1.3 完整代码

import{ RcTag }from'rchoui'@Entry@ComponentV2 struct ArticleDetailTagsDemo {// 文章元数据private articleCategory:string='鸿蒙开发'private articleTags:string[]=['ArkTS','ArkUI','组件库','UI设计','开源']private articleDifficulty:string='进阶'private articleDifficultyType:'primary'|'warning'|'error'='warning'build(){Column({ space:16}){// 文章标题Text('HarmonyOS6 组件库实战:RcTag 深度解析').fontSize(20).fontWeight(FontWeight.Bold).fontColor('#1a1a1a')// 标签区域Column({ space:8}){// 第一行:主分类(实色)+ 难度(胶囊)Row({ space:8}){RcTag({ rcTagText:this.articleCategory, rcTagType:'primary', rcTagSize:'medium'})RcTag({ rcTagText:this.articleDifficulty, rcTagType:this.articleDifficultyType, rcTagShape:'circle', rcTagSize:'mini'})}// 第二行:内容标签(镂空填充)Flex({ wrap: FlexWrap.Wrap }){ForEach(this.articleTags,(tag:string)=>{RcTag({ rcTagText: tag, rcTagType:'info', rcTagPlain:true, rcTagPlainFill:true, rcTagSize:'mini', rcTagMargin:{ right:6, bottom:6}})},(tag:string)=> tag)}}.alignItems(HorizontalAlign.Start).width('100%')// 文章摘要Text('本文深度解析 RcTag 组件的架构设计、色彩系统...').fontSize(14).fontColor('#606266').lineHeight(22)}.padding(16).width('100%').backgroundColor(Color.White)}}

1.4 代码详解

数据结构设计

组件定义了三个私有变量来存储文章元数据:articleCategory 是文章主分类,固定为字符串;articleTags 是内容关键词数组,数量动态;articleDifficultyType 的类型是联合类型 'primary' | 'warning' | 'error',这使得难度类型与 RcTagrcTagType 直接兼容,无需任何转换就可以透传给组件。这个设计细节值得学习:让数据层的类型定义与 UI 层的 Props 保持一致,能大幅减少映射代码。

第一行:主分类与难度标签

主分类标签只配置了 rcTagType: 'primary'rcTagSize: 'medium',未设置 rcTagShape,因此使用默认的方形(square),整体感觉更稳重。难度标签则配置了 rcTagShape: 'circle',呈现胶囊形状,视觉上比方形更活泼。rcTagSize: 'mini' 使难度标签明显小于主分类标签,这种尺寸差异主动建立了视觉层级。

两个标签放在 Row({ space: 8 }) 中,space: 8 在两标签之间产生 8vp 的间距,无需借助 rcTagMargin 来处理间距,代码更简洁。

第二行:内容关键词标签

内容关键词数组用 ForEach 遍历渲染,key 函数直接返回标签文本本身 (tag: string) => tag。这在标签文本唯一时是没问题的——文章的内容标签通常不会重复,这个假设是合理的。若标签可能重复,则应改用索引或其他唯一标识。

每个标签设置了 rcTagMargin: { right: 6, bottom: 6 },右边距和下边距共同作用于 Flex 换行场景:右边距控制同行标签间的水平间距,下边距则在换行后产生行间距,最终标签无论在哪一行哪一位置,与邻近元素的距离都是一致的 6vp。

rcTagPlain: true 开启镂空描边模式,rcTagPlainFill: true 在镂空基础上增加浅色背景填充。这两个属性必须同时设置才能产生镂空填充效果——单独设置 rcTagPlainFill 而不设置 rcTagPlain,浅色背景不会生效。rcTagType: 'info' 决定了浅色背景和描边的颜色基调,这里选用 info 是因为其灰蓝色调不强调、不抢眼,恰好适合次要信息的展示。

布局容器的 alignItems

标签区域的 Column 设置了 alignItems(HorizontalAlign.Start),这确保内部的 RowFlex 都左对齐。如果不设置,默认居中对齐,标签会出现在屏幕中央,与正文排版惯例不符。


二、场景二:商品分类筛选

2.1 场景描述

电商 App 的商品列表页,顶部提供分类筛选标签,支持单选切换,点击标签实时过滤商品列表。这是电商 App 中最常见的交互模式之一,分类标签横向排列,超出屏幕宽度时可以横向滑动,点击某分类后列表立即刷新。

2.2 设计思路

这个场景的技术核心有两点:一是用 rcTagPlain 的动态值来表达"选中/未选中"两种视觉状态,二是用 rcTagName 携带分类 ID,通过回调事件更新状态变量从而驱动列表过滤。

选中状态的实现思路是:当某个分类标签的 id 等于当前 selectedCategory 时,rcTagPlainfalse(实色,表示选中);否则为 true(镂空,表示未选中)。这样一行表达式 rcTagPlain: this.selectedCategory !== cat.id 就完整描述了选中状态,逻辑非常清晰。

2.3 完整代码

import{ RcTag }from'rchoui'interfaceProductCategory{ id:number label:string}interfaceProduct{ id:number name:string category:number price:string}@Entry@ComponentV2 struct ProductFilterDemo {@Local selectedCategory:number=0// 0 = 全部private categories: ProductCategory[]=[{ id:0, label:'全部'},{ id:1, label:'手机数码'},{ id:2, label:'家用电器'},{ id:3, label:'服装鞋包'},{ id:4, label:'美妆护肤'},{ id:5, label:'图书'}]private products: Product[]=[{ id:1, name:'HarmonyOS 手机', category:1, price:'3999'},{ id:2, name:'智能冰箱', category:2, price:'4299'},{ id:3, name:'运动夹克', category:3, price:'299'},{ id:4, name:'精华液套装', category:4, price:'599'},{ id:5, name:'ArkTS 实战', category:5, price:'89'},{ id:6, name:'鸿蒙平板', category:1, price:'2599'}]privategetfilteredProducts(): Product[]{if(this.selectedCategory ===0){returnthis.products }returnthis.products.filter(p => p.category ===this.selectedCategory)}build(){Column(){// 分类筛选栏Scroll(){Row({ space:8}){ForEach(this.categories,(cat: ProductCategory)=>{RcTag({ rcTagText: cat.label, rcTagType:'primary', rcTagShape:'circle', rcTagPlain:this.selectedCategory !== cat.id, rcTagName: cat.id,onRcTagClick:(name)=>{this.selectedCategory = name asnumber}})},(cat: ProductCategory)=>String(cat.id))}.padding({ left:16, right:16, top:12, bottom:12})}.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off).width('100%').backgroundColor(Color.White)// 商品列表List({ space:0}){ForEach(this.filteredProducts,(product: Product)=>{ListItem(){Row({ space:12}){Column({ space:4}){Text(product.name).fontSize(15).fontColor('#1a1a1a')Text(`¥${product.price}`).fontSize(14).fontColor('#f56c6c').fontWeight(FontWeight.Medium)}.alignItems(HorizontalAlign.Start).layoutWeight(1)// 状态标签RcTag({ rcTagText:this.categories.find(c => c.id === product.category)?.label ??'', rcTagType:'info', rcTagSize:'mini', rcTagPlain:true})}.padding({ left:16, right:16, top:14, bottom:14}).width('100%').backgroundColor(Color.White)}},(product: Product)=>String(product.id))}.divider({ strokeWidth:0.5, color:'#f0f0f0', startMargin:16, endMargin:16}).backgroundColor(Color.White).margin({ top:8})}.width('100%').height('100%').backgroundColor('#f5f5f5')}}

2.4 代码详解

接口定义

代码在组件外定义了 ProductCategoryProduct 两个接口。ProductCategoryid: number 作为分类的唯一标识,id: 0 被约定为"全部"这一特殊分类。Productcategory 字段存储的就是 ProductCategoryid,二者通过数字 ID 关联,这是一种轻量的关联数据结构,不引入复杂的嵌套。

状态变量设计

selectedCategory 使用 @Local 装饰,初始值为 0,对应"全部"分类。@Local 是 ComponentV2 中的局部状态装饰器,修改该变量会触发组件重新渲染,这是驱动筛选栏视觉更新和列表内容刷新的根本机制。

计算属性 filteredProducts

filteredProducts 定义为 private get,即 getter 计算属性。每次 build() 执行时都会调用它,根据当前 selectedCategory 过滤商品数组并返回结果。当 selectedCategory 为 0 时直接返回全量数据,否则用 Array.filter 过滤出匹配分类的商品。这个设计的好处是将过滤逻辑与 UI 布局代码分离,build() 里只需读取 filteredProducts 即可,无需关心过滤细节。

筛选栏的横向滚动

分类标签放在 Scroll 容器中,scrollable(ScrollDirection.Horizontal) 指定为横向滚动,scrollBar(BarState.Off) 隐藏滚动条(滚动条在手机端通常视觉上不美观)。内部使用 Row({ space: 8 }) 横向排列所有分类标签,设置了 padding 为标签四周预留呼吸空间。

rcTagPlain 驱动选中状态

这是本案例最核心的技术点。rcTagPlain: this.selectedCategory !== cat.id 这行代码的含义:当前分类的 ID 不等于已选分类 ID 时,标签处于镂空状态(未选中样式);等于时 rcTagPlainfalse,标签呈现实色填充(选中样式)。由于 selectedCategory@Local 状态变量,每次点击都会更新它,进而触发 ForEach 所有标签重新计算 rcTagPlain 的值,选中/未选中状态即时切换。

rcTagName 携带业务标识

rcTagName: cat.id 将分类 ID 作为标签的"名字"附加到组件上。onRcTagClick 回调的参数就是 rcTagName 的值,因此在回调中可以直接拿到被点击分类的 ID,执行 this.selectedCategory = name as number 完成状态更新。这避免了在回调闭包中捕获整个 cat 对象,是更干净的传参方式。

ForEach 的 key 函数

筛选栏的 ForEach 使用 (cat: ProductCategory) => String(cat.id) 作为 key 函数,商品列表使用 (product: Product) => String(product.id) 作为 key。数字 ID 需要通过 String() 转换为字符串,因为 ForEach 的 key 函数返回值类型是 string。用 ID 作为 key 的好处是:即使数组顺序发生变化,ArkTS 框架也能精确识别哪些节点需要更新,而不会触发全量重渲染,性能更优。

商品列表中的分类标签

商品列表每一行右侧也有一个小标签,显示该商品所属的分类名称。this.categories.find(c => c.id === product.category)?.label ?? '' 通过分类 ID 在分类数组中查找对应名称,?. 可选链保证了 find 返回 undefined 时不报错,?? '' 则提供了空字符串兜底。这个标签使用 rcTagType: 'info'rcTagPlain: truercTagSize: 'mini',视觉上轻量,不与左侧的商品主信息竞争注意力。

商品列表的布局权重

商品信息的 Column 设置了 .layoutWeight(1),这使其在 Row 中占据除右侧标签以外的所有剩余宽度。layoutWeight 是 ArkUI 中类似 CSS flex: 1 的属性,配合 Row 使用时效果等同于弹性布局中的按比例分配空间,保证标签始终靠右对齐,商品名称可以充分利用剩余空间。


总结

本文介绍的两个案例分别代表了 RcTag 的两种典型用法:纯展示(通过类型和风格传达信息层级)和交互筛选(通过状态变量与 rcTagPlain 联动实现选中态)。rcTagMargin 解决了多标签排列的间距问题,rcTagName + 事件回调解决了 ForEach 场景下的标识传递问题。这两个模式在大多数 App 的标签需求中都能复用。

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

Read more

AI 对话高效输入指令攻略(五):AI+PicDoc文生图表工具:解锁高效图表创作新范式

AI 对话高效输入指令攻略(五):AI+PicDoc文生图表工具:解锁高效图表创作新范式

非广告!!!!只是好用的软件推广而已!!!! 免责声明: 1.本文所提供的所有 AI 使用示例及提示词,仅用于学术写作技巧交流与 AI 功能探索测试,无任何唆使或鼓励利用 AI 抄袭作业、学术造假的意图。 2.文章中提及的内容旨在帮助读者提升与 AI 交互的能力,合理运用 AI 辅助学习和研究,最终成果的原创性与合规性需使用者自行负责。 3.对于读者因不当使用文中内容,违反学术规范、法律法规或造成其他不良后果的情况,本文作者及发布平台不承担任何责任。 目录 * 前言 * 一.介绍 * 1.软件介绍 * 2.适用群体 * 二.PicDoc文档使用教程:功能入口与操作指引 * 步骤1:登录账号 * 步骤2:新建文档 * 步骤3:编辑与生成可视化内容 * 步骤4:保存与导出 * 注意事项 * 三.核心功能深度测评 * 1.

By Ne0inhk
Qoder AI 编程全攻略:从安装到实战,小白也能轻松上手

Qoder AI 编程全攻略:从安装到实战,小白也能轻松上手

前言 还在觉得 AI 编程只是简单的代码补全?那你一定要试试Qoder!这款面向真实软件开发的 Agentic 编码平台,可不是普通的 AI 代码工具,它能深度理解你的整个代码库,把复杂的开发工作拆解开自动处理,不管是在 IDE 里无缝开发,还是在终端里高效操作,都能让你写代码的效率翻倍。 本文结合 Qoder 官方文档和实际使用经验,用最通俗的语言讲清 Qoder 的核心功能、安装步骤和实战用法,不管你是刚接触 AI 编程的新手,还是想提升开发效率的老程序员,都能轻松看懂、快速上手! 一、Qoder 是什么?核心亮点速览 Qoder(发音 /ˈkoʊdər/)是一款主打智能体驱动的 AI 编程平台,和普通的代码补全工具(比如 Copilot)相比,它的核心优势在于深度的项目上下文理解和自动化的复杂任务处理,简单说就是:它能 “读懂” 你的整个项目,

By Ne0inhk
Anthropic 最近经济指数报告 202603 解读——人工智能对劳动力市场的影响的初步证据

Anthropic 最近经济指数报告 202603 解读——人工智能对劳动力市场的影响的初步证据

划重点 * Anthropic引入了一种衡量人工智能替代风险的新指标——观测暴露度,该指标结合了理论上的大语言模型能力与实际使用数据,并更侧重于自动化(而非增强型)以及与工作相关的应用场景。 * 人工智能远未达到其理论能力:实际覆盖率仅为理论可行范围的一小部分。 * 美国劳工统计局预测,到2034年,观测暴露度较高的职业,其就业增长将相对较慢。暴露度最高职业中的从业者,更可能是年龄较大、女性、受教育程度较高且收入较高的群体。 * Anthropic发现,自2022年底以来,高暴露度工人的失业率并未出现系统性上升,但有初步证据表明,在暴露度较高的职业中,年轻工人的招聘速度有所放缓。 引言 人工智能的快速普及正引发大量研究,试图衡量和预测其对劳动力市场的影响。然而,过往研究方法的记录提醒我们应保持谦逊。不要慌张,不要烧包,不要踩踏,既不要盲信“2028人工智能替代导致经济危机”,也要理性看待已经在海平面上涌现的这波AI技术浪潮。 Anthropic基于其公布的经济指数,不同于去年下半年着重讨论不同地区和不同行业的AI采用率或自动化程度,在本文中,Anthropic开始讨论

By Ne0inhk
Vibe Coding范式实战:用AI工具链(Stitch+Figma+ai studio+Trae)快速开发全栈APP

Vibe Coding范式实战:用AI工具链(Stitch+Figma+ai studio+Trae)快速开发全栈APP

文章目录 * 概要 * stitch制作设计稿 * figma 原型展示 * ai studio 生成前端代码 * 基于trae + Supabase生成后端代码和数据库 * Github + vercel * pc端后台管理系统设计 概要 在 AI 技术深度渗透软件开发领域的当下,一种名为 “Vibe Coding”(氛围编程)的全新范式正在重塑开发者的工作方式。它的核心在于,开发者不再是逐行编写代码的 “码农”,而是通过自然语言描述意图、引导 AI 生成代码的 “创意引导者” 和 “结果验证者”,从而将精力聚焦于更高价值的产品设计和逻辑思考上。 本文提供一种 Vibe Coding 的工作模式:设计阶段以 Google Stitch 为起点,开发者通过文本或草图快速生成响应式 UI 设计与前端代码,再无缝导入 Figma 进行精细化视觉调整和原型设计,实现了从 “想法” 到

By Ne0inhk