鸿蒙新特性:标签管理与搜索筛选——构建动态标签系统
标签(Tag)是现代应用中无处不在的信息组织方式。从 Gmail 的邮件分类标签、Jira 的任务状态标记、Notion 的页面属性标签到微信的联系人分组——标签系统帮助用户在大量信息中快速分类、筛选和检索。
HarmonyOS NEXT ArkUI 虽然没有独立的"Tag 组件",但通过基础组件的组合(TextInput、Grid、Text、Row),可以构建出功能完整的标签管理系统。本文将深入讲解如何用基础组件构建标签系统,并通过一个"标签管理工具"实战案例展示添加、删除、搜索、多选和批量操作等功能。
关键词:HarmonyOS、ArkUI、标签管理、搜索筛选、多选操作、组件组合
一、标签系统的设计目标
一个实用的标签管理系统需要支持以下操作:
- 添加标签:用户输入文字快速创建新标签,自动分配颜色
- 删除标签:每个标签有删除入口,支持逐个删除
- 搜索筛选:在大量标签中快速定位目标标签
- 多选操作:选中多个标签进行批量操作(如批量删除)
这些操作看似简单,但背后的交互设计需要考虑:输入验证、空状态处理、搜索匹配策略、选中状态视觉反馈、删除操作的不可逆性等。
二、数据模型与状态管理
2.1 标签数据结构
每个标签包含唯一 ID、文本内容、颜色、选中状态:
interface TagItem {
id: number;
text: string; // 标签文字
color: string; // 标签颜色
selected: boolean; // 是否被选中
}
颜色使用预设调色板循环分配,确保每个新标签都有可区分的颜色:
private tagColors: string[] = [
'#1677FF', '#00B894', '#E09F3E', '#6C5CE7', '#FF6B6B', '#0984E3'
];
addTag(): void {
let text = this.inputText.trim();
if (text.length === 0) return;
let copy = this.tags.slice();
copy.push({
id: this.nextId++,
text: text,
color: this.tagColors[this.colorIdx % this.tagColors.length],
selected: false
});
this.tags = copy;
this.colorIdx++;
this.inputText = '';
}
6 种颜色轮换使用,相邻创建的标签颜色不同,在视觉上自然形成区分。不要求同色标签不连续(6 种颜色足够分散),只需保证最基本的可区分性。
2.2 状态更新的数组模式
延续上一篇文章的教训,所有修改 @State tags 数组的方法都使用 slice() + 重新赋值的模式:
deleteTag(id: number): void {
let copy = this.tags.slice();
let target = -1;
for (let i = 0; i < copy.length; i++) {
if (copy[i].id === id) { target = i; break; }
}
if (target !== -1) {
copy.splice(target, 1);
this.tags = copy; // 新数组引用触发状态更新
}
}
toggleSelect 方法在切换选中状态时,需要创建修改元素的新对象(而非修改原对象属性):
toggleSelect(id: number): void {
let copy = this.tags.slice();
for (let i = 0; i < copy.length; i++) {
if (copy[i].id === id) {
copy[i] = {
id: copy[i].id, text: copy[i].text, color: copy[i].color,
selected: !copy[i].selected // 新对象,新 selected 值
};
break;
}
}
this.tags = copy;
}
这里的关键是:不仅需要新数组引用(slice()),还需要被修改的元素有新的对象引用(手动创建新对象)。两个层级的引用更新确保了 @State 和 ForEach 能正确检测到变化。
三、搜索筛选
3.1 搜索输入
搜索栏使用 TextInput 组件,通过 onChange 回调实时更新 searchText 状态:
TextInput({ placeholder: '搜索标签...' })
.fontSize(13)
.layoutWeight(1)
.height(36)
.backgroundColor('#F5F5F5')
.borderRadius(8)
.onChange((value: string) => { this.searchText = value; })
搜索是"即时筛选"而非"点击搜索按钮后筛选"——每输入一个字符,标签网格立即更新。这种模式适合标签总数在 100 以内的场景。如果标签数量上千,需要加防抖(debounce 300ms)避免高频渲染。
3.2 筛选逻辑
getFiltered(): TagItem[] {
if (!this.searchText || this.searchText.length === 0) {
return this.tags; // 无搜索词 → 返回全部
}
let result: TagItem[] = [];
for (let i = 0; i < this.tags.length; i++) {
if (this.tags[i].text.indexOf(this.searchText) !== -1) {
result.push(this.tags[i]);
}
}
return result;
}
使用简单的 indexOf 子串匹配——输入"工作"可以匹配"工作中"和"日常工作"。这是最直观的搜索策略,不需要用户了解正则或通配符。
搜索状态下,页面显示当前搜索结果数量和快速操作入口:
搜索: "项目" — 3 个结果 删除这些
"删除这些"按钮允许用户一次性删除当前搜索匹配的所有标签。这是一个强大的批量操作——先搜索定位,再统一删除。比逐个寻找和删除高效得多。
四、多选操作
4.1 选中交互
点击标签切换选中状态。选中的标签视觉反馈是双重的:
// 选中标签的样式
.backgroundColor(tag.selected ? tag.color : tag.color.concat('18'))
.border({
width: tag.selected ? 2 : 1,
color: tag.selected ? tag.color : tag.color.concat('40'),
style: BorderStyle.Solid
})
未选中时:标签使用非常淡的背景色(主题色 + 18 即 9% 透明度)+ 细边框(主题色 + 40 即 25% 透明度),文字为深色。这是一种"轻量"的视觉效果——标签可见但不夺目。
选中时:标签使用实色背景(主题色 100% 不透明)+ 粗边框(2vp),文字变为白色。这是"高亮"效果——选中标签从标签群中脱颖而出。
4.2 选中计数与清除
选中至少一个标签后,操作栏自动出现:
if (this.getSelectedCount() > 0) {
Row() {
Text('已选 ' + this.getSelectedCount().toString() + ' 个')
.fontSize(12).fontColor('#1677FF').fontWeight(FontWeight.Medium)
Blank()
Text('清除选择')
.fontSize(12).fontColor('#FF4D4F')
.padding({ top: 4, bottom: 4, left: 10, right: 10 })
.borderRadius(10).backgroundColor('#FFF2F0')
.onClick(() => { this.clearSelection(); })
}
}
操作栏的显示和隐藏完全由状态驱动——if (this.getSelectedCount() > 0) 条件渲染。ArkUI 支持条件渲染,当选中数从 0 变为 1 时操作栏出现,从 1 变为 0 时消失。无额外过渡动画,但清晰直观。
clearSelection() 的实现遍历所有标签,将每个选中标签替换为新对象(selected: false)。为避免不必要的状态更新,只有当确实有标签被清除时才重新赋值 this.tags。
五、标签网格布局
使用 3 列 Grid 展示标签,每个标签是一个圆角矩形:
Grid() {
ForEach(this.getFiltered(), (tag: TagItem) => {
GridItem() {
Row() {
Text(tag.text)
.fontSize(13)
.fontColor(tag.selected ? '#FFFFFF' : '#1a1a2e')
.layoutWeight(1)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text('✕')
.fontSize(11)
.fontColor('#9999AA')
.padding({ left: 6 })
.onClick(() => { this.deleteTag(tag.id); })
}
.width('100%').padding({ top: 10, bottom: 10, left: 12, right: 8 })
.borderRadius(8)
.onClick(() => { this.toggleSelect(tag.id); })
}
}, (tag: TagItem) => tag.id.toString())
}
.columnsTemplate('1fr 1fr 1fr')
.columnsGap(8).rowsGap(8)
每个标签项有两个可点击区域:
- 标签主体(
Row的onClick):切换选中状态 - 删除按钮(
✕):删除标签
两个点击区域的处理不同——删除按钮的 onClick 事件不会冒泡到父级(ArkUI 的事件模型默认不冒泡),所以点击 ✕ 只会触发删除,不会触发选中。
六、空状态与边界处理
6.1 搜索无结果
当搜索词匹配不到任何标签时,显示友好的空状态提示:
if (this.getFiltered().length === 0) {
Text('没有匹配的标签,试试修改搜索条件')
.fontSize(13).fontColor('#BBBBCC')
.width('100%').textAlign(TextAlign.Center)
.padding({ top: 20, bottom: 20 })
}
灰色文字居中显示,提示用户调整搜索条件。空状态是用户体验的关键细节——不要留白,给用户明确指引。
6.2 空标签输入防护
addTag(): void {
let text = this.inputText.trim();
if (text.length === 0) {
return; // 忽略空白输入
}
// ... 添加逻辑
}
trim() 去除首尾空格后检查长度。纯空格输入(如连续空格)会被拦截,不创建空标签。
6.3 预设标签
页面首次加载时提供 7 个预设标签(工作、个人、紧急、学习、项目A、待跟进、收藏),让用户进入页面后立即看到标签系统的展示效果:
aboutToAppear(): void {
let preset: string[] = ['工作', '个人', '紧急', '学习', '项目A', '待跟进', '收藏'];
// 用预设数据初始化 this.tags
}
预设标签覆盖了常见的分类维度(优先级、场景、状态),为用户提供了标签命名的参考。用户可以直接使用这些预设,也可以删除它们创建自己的体系。
七、完整交互流程
- 初始状态:7 个预设标签以 3 列网格展示,颜色各异,无选中
- 添加标签:在顶部输入框输入"设计稿",点击"添加",新标签(橙色)出现在网格末尾
- 搜索筛选:在搜索栏输入"项目",网格只显示"项目A",提示"1 个结果"
- 选中标签:点击"紧急"和"待跟进",两个标签变为实色背景 + 白字,操作栏显示"已选 2 个"
- 清除选择:点击"清除选择",所有标签恢复默认样式,操作栏消失
- 删除标签:点击"项目A"的 ✕,该标签从网格中消失,总数减少
- 批量删除:搜索"工作",结果中出现"工作",点击"删除这些",该标签被删除,搜索栏清空
八、进阶扩展方向
8.1 标签编辑
当前标签文字不可修改。扩展为点击标签文字触发编辑模式——文字变为 TextInput,修改后保存。配合 onBlur 或回车完成编辑。
8.2 标签拖拽排序
配合 onItemDragStart / onItemDrop 事件,支持长按标签卡片拖动到目标位置重新排序。标签的排列顺序在某些场景中是语义化的(如优先级从高到低)。
8.3 标签分组
将标签按类别分组(如"工作相关"、“个人相关”),每组有标题和可折叠的标签列表。这对标签数量超过 50 的复杂系统尤为重要。
8.4 标签颜色自定义
当前颜色由系统自动轮换分配。扩展为点击颜色圆点弹出颜色选择器,用户可以自定义标签颜色。配合 PersistentStorage 持久化颜色配置。
8.5 标签关联数据
在实际应用中,标签通常关联到具体的数据对象(如任务、笔记、联系人)。扩展为"标签 → 数据对象"的关联关系和反向查询(点击标签查看所有关联的数据项)。
九、总结
本文通过"标签管理工具"这个实战案例,展示了如何用基础 ArkUI 组件(TextInput、Grid、Row、Text)构建功能完整的标签系统。核心知识点包括:
- 数据模型设计:标签的字段选择——id、text、color、selected
- 状态更新模式:
slice()+ 新对象 → 新数组引用 → 触发@State更新 - 搜索筛选策略:即时筛选 + 子串匹配,适合中小规模数据集
- 多选交互:视觉反馈(双层样式变化)、计数显示、清除操作
- 批量操作:结合搜索的"删除这些"——先筛选定位,再批量删除
- 空状态处理:搜索无结果的友好提示、空白输入拦截
- 颜色编码:6 色调色板轮换分配、选中态的实色/半透明双重样式
标签系统看似简单,但它包含了 CRUD 操作(增删改查)、搜索筛选、多选管理和批量操作——这些是几乎每个管理后台和效率工具都需要的基础功能。掌握了这些模式,你可以将它们应用到任务分类、内容标记、联系人分组、商品属性标签等任何需要"轻量级数据组织"的场景。
更多推荐



所有评论(0)