TinyRobot SuggestionPopover智能建议弹出框组件

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 },其中 itemSuggestionItem 类型的对象,你可以携带额外的自定义字段用于渲染。

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 为空数组时自动显示空状态。同时提供 loadingempty 插槽支持自定义这两种状态的展示内容。

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 插槽

除了 itemloadingempty 插槽外,SuggestionPopover 还提供了 headerbody 插槽,用于对弹出框的头部和内容区域进行完全自定义。

弹出框的插槽结构如下:

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 ⭐)

相关推荐
LiuJun2Son1 小时前
Angular 快速入门:从零搭建你的第一个应用
前端·javascript·angular.js
小徐_23331 小时前
Wot UI 2.1.0 发布:ConfigProvider 全局配置能力升级
前端·uni-app
方白羽1 小时前
Vibe Coding 四个核心阶段
android·前端·app
奶油话梅糖1 小时前
浏览器解析 HTML 头部的底层逻辑:从字节流到资源调度
前端·html
YHL1 小时前
🚀从零理解树与二叉树 —— 概念、实现与遍历
前端·javascript·数据结构
小时前端1 小时前
微前端技术选型深度分析:从概念到实践
前端
wyhwust2 小时前
基于Apifox的接口管理工具
前端
柒和远方2 小时前
后端认证、鉴权、高并发:从 Session 到 JWT 再到 Redis
前端·后端·面试
piglet121382 小时前
把搜索调到 Claude.ai 的水准
前端·人工智能