TinyRobot SuggestionPopover:智能建议弹出框组件
在 AI 对话界面中,用户常常需要引导式的交互体验。当 AI 助手需要向用户提供一组可选的建议项时,一个优雅的弹出框可以兼顾信息密度和操作便捷性。SuggestionPopover 正是为这种场景设计的组件------它在用户点击触发器后弹出一个浮层,展示建议列表,帮助用户快速定位和选择所需操作。
SuggestionPopover 的核心价值在于:
- 引导式交互:将建议项集中在弹出框中,避免界面杂乱
- 上下文帮助:在对话流程中提供及时的建议选项
- 灵活的数据结构:支持平铺列表和分组列表两种数据格式
- 丰富的自定义:提供多个插槽,支持自定义触发器、列表项、头部、内容区等
- 状态管理:内置 loading 和 empty 状态
- 移动端适配:窗口宽度小于 768px 时自动适配移动端样式
基本用法:带数据的弹出框
通过 data 属性传入建议数据,使用 trigger 插槽自定义触发器。点击触发器即可弹出建议列表。
vue
<template>
<TrSuggestionPopover :data="data" @item-click="handleItemClick">
<template #trigger>
<button>点击弹出建议</button>
</template>
</TrSuggestionPopover>
</template>
<script setup lang="ts">
import { TrSuggestionPopover } from '@opentiny/tiny-robot'
const data = [
{ id: '1', text: '什么是弹性云服务器?' },
{ id: '2', text: '如何登录到Windows云服务器?' },
{ id: '3', text: '弹性公网IP为什么ping不通?' },
{ id: '4', text: '云服务器安全组如何配置?' },
{ id: '5', text: '如何查看云服务器密码?' },
]
const handleItemClick = (item: any) => {
console.log('选中建议项:', item)
}
</script>
触发方式:click vs manual
SuggestionPopover 支持两种触发方式,通过 trigger 属性配置:
'click'(默认):点击触发器时自动弹出/关闭弹出框'manual':需要手动通过show属性(支持v-model)控制弹出框的显示和隐藏
vue
<template>
<div style="display: flex; gap: 8px">
<!-- click 触发 -->
<TrSuggestionPopover
:data="data"
trigger="click"
@open="console.log('open')"
@close="console.log('close')"
>
<template #trigger>
<button>click 触发</button>
</template>
</TrSuggestionPopover>
<!-- manual 触发 -->
<TrSuggestionPopover
:data="data"
v-model:show="manualShow"
trigger="manual"
@close="handleClose"
>
<template #trigger>
<button @click="manualShow = !manualShow">manual 触发</button>
</template>
</TrSuggestionPopover>
</div>
</template>
<script setup lang="ts">
import { TrSuggestionPopover } from '@opentiny/tiny-robot'
import { ref } from 'vue'
const manualShow = ref(false)
const data = [
{ id: '1', text: '什么是弹性云服务器?' },
{ id: '2', text: '如何登录到Windows云服务器?' },
{ id: '3', text: '弹性公网IP为什么ping不通?' },
]
const handleClose = () => {
manualShow.value = false
}
</script>
manual 模式适用于需要根据业务逻辑精确控制弹出框显示时机的场景,例如在 AI 回复完成后自动弹出建议。
分组建议数据
当建议项较多且可以按类别划分时,可以使用分组数据格式。在 data 数组中传入 SuggestionGroup 对象,通过 group 字段标识分组,items 字段包含该分组下的建议项。
vue
<template>
<TrSuggestionPopover
:data="groups"
v-model:selectedGroup="selectedGroup"
@item-click="handleItemClick"
@group-click="handleGroupClick"
>
<template #trigger>
<button>分组建议</button>
</template>
</TrSuggestionPopover>
</template>
<script setup lang="ts">
import { TrSuggestionPopover } from '@opentiny/tiny-robot'
import { IconLike, IconDislike } from '@opentiny/tiny-robot-svgs'
import { ref } from 'vue'
const selectedGroup = ref('purchase')
const groups = [
{
group: 'basic',
label: '推荐',
icon: IconLike,
items: [
{ id: 'b1', text: '什么是弹性云服务器?' },
{ id: 'b2', text: '如何登录到Windows云服务器?' },
{ id: 'b3', text: '弹性公网IP为什么ping不通?' },
],
},
{
group: 'purchase',
label: '购买咨询',
icon: IconDislike,
items: [
{ id: 'p1', text: '如何购买弹性云服务器?' },
{ id: 'p2', text: '云服务器价格怎么计算?' },
{ id: 'p3', text: '如何续费云服务器?' },
],
},
{
group: 'usage',
label: '使用咨询',
icon: IconLike,
items: [
{ id: 'u1', text: '云服务器使用限制与须知' },
{ id: 'u2', text: '如何重置云服务器密码?' },
{ id: 'u3', text: '云服务器如何安装软件?' },
],
},
]
const handleItemClick = (item: any) => {
console.log('选中项:', item)
}
const handleGroupClick = (group: any) => {
console.log('切换分组:', group)
}
</script>
注意 :分组数据和普通(非分组)数据不能混合使用,data 数组中的项要么全是 SuggestionItem,要么全是 SuggestionGroup。
selectedGroup 支持 v-model,可以设置默认选中的分组,也可以在用户切换分组时获取当前选中分组。groupShowMoreTrigger 属性控制分组"显示更多"的触发方式,支持 'click' 和 'hover'。
自定义列表项渲染
通过 item 插槽可以完全自定义每个建议项的渲染方式。插槽参数为 { item },其中 item 是 SuggestionItem 类型的对象,你可以携带额外的自定义字段用于渲染。
vue
<template>
<TrSuggestionPopover :data="data" @item-click="handleItemClick">
<template #trigger>
<button>自定义列表项</button>
</template>
<template #item="{ item }">
<div style="display: flex; align-items: center; gap: 8px">
<span style="color: #1476ff; font-weight: 600">{{ item.tag }}</span>
<span>{{ item.text }}</span>
</div>
</template>
</TrSuggestionPopover>
</template>
<script setup lang="ts">
import { TrSuggestionPopover } from '@opentiny/tiny-robot'
const data = [
{ id: '1', text: '什么是弹性云服务器?', tag: '热门' },
{ id: '2', text: '如何登录到Windows云服务器?', tag: '常见' },
{ id: '3', text: '弹性公网IP为什么ping不通?', tag: '新问题' },
]
const handleItemClick = (item: any) => {
console.log('选中项:', item)
}
</script>
加载和空状态
SuggestionPopover 内置了 loading 和 empty 两种状态的展示。通过 loading 属性显示加载状态,当 data 为空数组时自动显示空状态。同时提供 loading 和 empty 插槽支持自定义这两种状态的展示内容。
vue
<template>
<div style="display: flex; gap: 8px">
<!-- loading state -->
<TrSuggestionPopover :data="[]" :loading="true">
<template #trigger>
<button>加载中</button>
</template>
<template #loading>
<div style="padding: 20px; text-align: center; color: #999">
正在获取建议...
</div>
</template>
</TrSuggestionPopover>
<!-- empty state -->
<TrSuggestionPopover :data="[]">
<template #trigger>
<button>空状态</button>
</template>
<template #empty>
<div style="padding: 20px; text-align: center; color: #999">
暂无建议项
</div>
</template>
</TrSuggestionPopover>
</div>
</template>
<script setup lang="ts">
import { TrSuggestionPopover } from '@opentiny/tiny-robot'
</script>
Header 和 Body 插槽
除了 item、loading、empty 插槽外,SuggestionPopover 还提供了 header 和 body 插槽,用于对弹出框的头部和内容区域进行完全自定义。
弹出框的插槽结构如下:
sql
+---------------------------+
| SuggestionPopover |
| +---------------------+ |
| | header | |
| +---------------------+ |
| | | |
| | body | |
| | +-------------+ | |
| | | item[] | | |
| | +-------------+ | |
| | | |
| | loading / empty | |
| +---------------------+ |
+---------------------------+
- 当使用
header插槽时,将替换默认的标题区域 - 当使用
body插槽时,将替换整个列表区域(包括 item 列表、loading 和 empty 状态)
vue
<template>
<TrSuggestionPopover
:data="data"
v-model:show="show"
trigger="manual"
>
<template #trigger>
<button @click="show = !show">完全自定义</button>
</template>
<template #header>
<h3 style="font-size: 18px; font-weight: 600; margin: 0">
常见问题
</h3>
</template>
<template #body>
<ul style="list-style: none; padding: 0; margin: 0">
<li
v-for="item in data"
:key="item.id"
style="padding: 8px 12px; cursor: pointer"
@click="handleItemClick(item)"
>
<span style="color: #1476ff">{{ item.text }}</span>
</li>
</ul>
</template>
</TrSuggestionPopover>
</template>
<script setup lang="ts">
import { TrSuggestionPopover } from '@opentiny/tiny-robot'
import { ref } from 'vue'
const show = ref(false)
const data = [
{ id: '1', text: '什么是弹性云服务器?' },
{ id: '2', text: '如何登录到Windows云服务器?' },
{ id: '3', text: '弹性公网IP为什么ping不通?' },
]
const handleItemClick = (item: any) => {
console.log('选中项:', item)
show.value = false
}
</script>
移动端适配
SuggestionPopover 内置了移动端适配逻辑。当视窗宽度小于 768px 时,弹出框会自动切换为移动端友好的展示样式,包括更大的触控区域和适配小屏幕的布局。
开发者无需额外配置,只需确保页面有正确的 viewport 设置即可:
html
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
API 参考
Props
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
data |
SuggestionData |
是 | - | 建议数据 |
title |
string |
否 | - | 弹出框标题 |
icon |
`VNode | Component` | 否 | - |
show |
boolean |
否 | - | 控制弹出框显示/隐藏,仅在 trigger 为 'manual' 时有效 |
trigger |
`'click' | 'manual'` | 否 | 'click' |
selectedGroup |
string |
否 | - | 当前选中分组,支持 v-model |
groupShowMoreTrigger |
`'click' | 'hover'` | 否 | - |
loading |
boolean |
否 | false |
是否显示加载状态 |
topOffset |
number |
否 | - | 顶部偏移量 |
Slots
| 插槽名 | 类型 | 说明 |
|---|---|---|
trigger |
`() => VNode | VNode[]` |
item |
`({ item }: { item: SuggestionItem }) => VNode | VNode[]` |
loading |
`() => VNode | VNode[]` |
empty |
`() => VNode | VNode[]` |
header |
`() => VNode | VNode[]` |
body |
`() => VNode | VNode[]` |
Events
| 事件名 | 参数 | 说明 |
|---|---|---|
item-click |
item: SuggestionItem |
点击建议项时触发 |
group-click |
group: SuggestionGroup |
点击分组时触发 |
open |
- | 弹窗打开时触发 |
close |
- | 弹窗关闭时触发 |
click-outside |
event: MouseEvent |
点击弹窗外部区域时触发 |
Types
typescript
interface SuggestionItem {
id: string
text: string
}
interface SuggestionGroup {
group: string
label: string
icon?: VNode | Component
items: SuggestionItem[]
}
type SuggestionData = (SuggestionItem | SuggestionGroup)[]
OpenTiny NEXT 是一套企业智能前端开发解决方案,以生成式 UI 和 WebMCP 两大核心技术为基础,对现有传统的 TinyVue 组件库、TinyEngine 低代码引擎等产品进行智能化升级,构建出面向 Agent 应用的前端 NEXT-SDKs、AI Extension、TinyRobot智能助手、GenUI等新产品,加速企业应用的智能化改造,实现AI理解用户意图自主完成任务。
欢迎加入 OpenTiny 开源社区。添加微信小助手:opentiny-official 一起参与交流前端技术~
OpenTiny 官网:opentiny.design/ TinyRobot 代码仓库:github.com/opentiny/ti... (欢迎star ⭐) TinyRobot skill源码:github.com/opentiny/ag... (欢迎 Star ⭐)