npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

leatop-ai

v1.5.7

Published

Leatop AI 组件库:AI 悬浮助手(Agent)、AI 对话页(AiChat)、知识库(KnowledgeBase)、常见问题(Question)

Readme

AI 组件库使用文档

本包将 AI 悬浮助手(Agent,右下角小人 + 对话弹窗)、AI 对话页面(AiChat)、知识库(KnowledgeBase)、常见问题(Question)以 Vue3 组件形式导出, 供宿主系统直接引入。不含路由、不含登录:路由跳转由宿主控制,token 由宿主注入。

效果示例

Agent 悬浮助手:右下角知识 IP 小人问候,点击后以圆弧扩散动画弹出对话窗口:

Agent 悬浮助手演示


一、安装与前置要求

pnpm add leatop-ai --registry https://registry.npmjs.org/

宿主必须已安装以下依赖(peerDependencies,组件库不重复打包,均为宿主项目常见依赖):

| 依赖 | 版本要求 | | ----------------------- | ----- | | vue | ^3.3 | | element-plus | >=2.4 | | @element-plus/icons-vue | ^2.0 | | pinia | ^2.0 | | axios | ^1.0 | | @vueuse/core | ^10.0 | | dayjs | ^1.11 | | echarts | ^5.4 |

其余依赖(docx-preview、@vue-office/excel、@vue-office/pptx、pdfjs-dist、markdown-it、marked、x-markdown-vue、@pureadmin/table、sortablejs、screenfull、js-cookie、qs)为组件库功能性依赖,声明在 dependencies 中,安装本包时自动装好,宿主无需手动安装。

组件库内部使用独立的 Pinia 实例,与宿主的 Pinia 完全隔离:宿主无需(也不应)注册组件库的 store, 即使宿主存在同名 store(如登录的 user)也互不冲突。

二、全局配置(宿主必须调用)

在宿主入口(如 main.ts)调用一次 configureAi,组件库所有请求统一从该配置读取:

import { configureAi } from 'leatop-ai';

configureAi({
  // 接口基础地址:两种写法均支持
  // 1. 完整地址:'https://xxx.com/api/'(跨域时由后端开 CORS 或宿主网关转发)
  // 2. 相对路径:'/api/'(由宿主的 dev proxy / nginx 转发到后端)
  baseURL: '/api/',

  // token 获取函数:从宿主自己的登录态读取(必配,否则请求不带 token、
  // 也不拉取用户信息;组件库不做任何 cookie/localStorage 兜底)
  getToken: () => authStore.token,

  // 可选:请求携带 token 的 header key,默认 'cdp-token',可按后端约定自定义
  // tokenKey: 'Authorization',

  // 可选:自定义用户信息获取(异步函数,resolve 组件库所需字段即可)。
  // 组件库主要使用 account(历史列表/收藏的 userId)与 userName(侧栏欢迎语),
  // 其余字段按需返回。不传则默认由组件库根据 token 调用 /system/user/curUser
  // getUserInfo: async () => {
  //   const res = await myApi.curUser();
  //   return { account: res.account, userName: res.userName, avatar: res.avatar };
  // },

  // 可选:Agent 界面开关(默认都为 true)
  uiConfig: {
    showSettingButton: true,    // 侧边栏"设置"入口
    showFullscreenButton: true, // 全屏按钮
    showUploadButton: true,     // 输入区上传文件按钮(+),传 false 隐藏
    showVoiceButton: true,      // 输入区语音(麦克风)按钮,传 false 隐藏
    assistantAvatar: '',        // AI 消息头像外部 URL(不传/空则用包内默认头像)
    inputPlaceholder: '发消息或者按住空格说话...', // 未选中技能时的输入框占位文案
  },

  // 可选:接口 401 时的回调(如跳转宿主登录页)
  onUnauthorized: () => router.push('/login'),

  // 可选:技能列表(不传 = 内置两个技能,详见下方「技能与请求参数自定义」)
  // skills: ['1', '2'],

  // 可选:发送前按当前技能注入额外请求参数
  // getExtraParams: ({ skill }) => ({ bizId: myStore.bizId }),

  // 可选:发送前校验(返回 false 静默阻断、字符串阻断并提示、true/undefined 放行,支持 Promise)
  // validateSend: (payload, context) => payload.text ? undefined : '请输入内容',
});

用户信息默认不需要配置传递:Agent 组件挂载时会自动根据注入的 token 调用 /system/user/curUser 获取昵称、头像、组织等信息(无缓存,每次挂载拉取一次)。 若后端接口路径/结构不一致,可配置 getUserInfo(异步函数)由宿主自行获取并返回 组件库所需字段,配置后组件库不再调用该接口。

技能与请求参数自定义(可选)

输入区技能条默认展示知识问答与智能问数两个内置技能。type 是与后端的契约值,发送消息时作为 type 参数随 /ai/stream/chat 提交,内置写死的对应关系如下:

| type(写死) | 名称 | 图标(包内雪碧图) | 是否有知识库下拉 | | -------- | ------ | ----------- | -------- | | 1 | 知识问答 | knowledge-qa | 有 | | 2 | 智能问数 | smart-query | 无 |

knowledgeBase 不再作为宿主配置项:内置「知识问答(type 1)」的知识库下拉为包内内部行为,改名/改图标不影响;自定义技能需要选择类交互(下拉、级联、日期等)时,用 render 自行实现(见下文「自定义技能渲染组件」)。 使用 1 / 2 以外的 type 时,需后端支持该技能编号,否则消息无法正确路由。

三种用法(数组顺序即技能条的展示顺序):

// 1) 不传:使用内置两个技能
configureAi({ baseURL, getToken });

// 2) 字符串数组:只保留命中的内置技能(下例隐藏「知识问答」,只留「智能问数」)
configureAi({ skills: ['2'] });

// 3) 对象数组:覆盖内置技能字段,或新增自定义技能
configureAi({
  skills: [
    { type: '1', name: '业务问答' },                 // 命中内置 type 1:只改名称,其余字段沿用内置
    { type: '2', iconUrl: '/icons/query.png' },      // 命中内置 type 2:换成宿主自己的图标图片
    { type: '9', name: '智能报告', iconUrl: 'https://xxx.com/report.png',
      placeholder: '描述你要生成的报告', finishMessage: '报告已生成' }, // 未命中内置:全新技能
  ],
});

| 字段 | 类型 | 必填 | 说明 | | --------------- | ------- | -- | ----------------------------------------------- | | type | String | 是 | 技能标识,随 /ai/stream/chat 提交后端 | | name | String | 否 | 展示名称;仅当 type 未命中内置技能时必填 | | icon | String | 否 | 包内雪碧图图标名(不含 .svg,如 knowledge-qa) | | iconUrl | String | 否 | 自定义图标图片地址(http / 相对路径 / dataURL),优先于 icon | | placeholder | String | 否 | 选中该技能后的输入框占位文案 | | finishMessage | String | 否 | 该技能对话结束时的提示语 | | render | Component / Function | 否 | 技能激活后渲染在技能标识后面的自定义组件,支持组件定义或工厂函数,详见「自定义技能渲染组件(render)」 |

输入框占位文案优先级:选中技能的 placeholder → uiConfig.inputPlaceholder → 包内默认「发消息或者按住空格说话...」。技能未配置 placeholder 时会自动落到全局配置,两者都不用改代码。

注入额外请求参数:自定义技能通常需要宿主自己的参数(如数据源 id、指标编码),用 getExtraParams 在发送前按当前技能注入,同名参数以宿主返回值为准:

configureAi({
  getExtraParams: ({ skill }) =>
    skill?.type === '9' ? { dataSourceId: myStore.dataSourceId } : {},
});

自定义技能渲染组件(render)

技能激活后,在技能标识后面渲染宿主传入的自定义组件(选择器、表单面板等)。组件通过 props 拿到一个可直接赋值的参数对象,写入的值在发送消息时自动并入请求参数:

import { defineAsyncComponent } from 'vue';
import ReportPanel from './ReportPanel.vue';

configureAi({
  skills: [
    '1',
    {
      type: '9',
      name: '智能报告',
      iconUrl: 'https://xxx.com/report.png',

      // 写法一:直接传组件定义(最常用)
      render: ReportPanel,

      // 写法二:工厂函数,接收上下文 { skill, params },返回组件。
      // 适合按技能动态挑选组件,或在组件挂载前给双向参数设初始值:
      // render: ({ skill, params }) => {
      //   params.reportType = 'weekly';   // 直接赋值,发送时随消息携带
      //   params.source = skill.type;     // 也可读取上下文里的技能信息
      //   return ReportPanel;
      // },

      // 写法三:工厂函数 + 异步组件(组件体量较大时按需加载)
      // render: () => defineAsyncComponent(() => import('./ReportPanel.vue')),
    },
  ],
});
<!-- ReportPanel.vue:params 直接赋值即"双向",发送时自动携带 -->
<script setup>
const props = defineProps({
  params: { type: Object, required: true }, // 双向参数对象
  skill:  { type: Object, default: null },  // 当前激活的技能
});
</script>

<template>
  <el-select v-model="params.reportType" placeholder="报告类型" style="width: 120px">
    <el-option label="日报" value="daily" />
    <el-option label="周报" value="weekly" />
  </el-select>
</template>

规则说明:

  • 组件也可写成工厂函数 render: ({ skill, params }) => Component(支持返回 defineAsyncComponent 异步组件);
  • 发送时参数合并优先级:knowledgeIds(内置知识库)→ render.params → getExtraParams(宿主函数最高,同名覆盖);
  • 切换技能 / 清空输入时 params 由组件库重置,发送后保留(支持连续追问同条件);
  • 小窗(500px)与主输入区(1000px)切换时 params 随草稿自动互通,组件实例销毁不丢值。

技能区整体接管(#skills 插槽)

需要完全自定义技能条交互(自带 UI 组件库、复杂选中逻辑等)时,在 Agent / AiChat 上使用 #skills 插槽,技能区整块由宿主实现(上传「+」按钮与分隔线保留,可通过 uiConfig.showUploadButton 隐藏):

<template>
  <Agent>
    <template #skills="{ skills, selectedSkill, selectSkill, params }">
      <MySkillBar
        :skills="skills"
        :selected="selectedSkill"
        @select="selectSkill"
      />
    </template>
  </Agent>
</template>

插槽作用域:

| 参数 | 类型 | 说明 | | --- | --- | --- | | skills | AiSkill[] | 技能列表(响应式,来自 configureAi({ skills })) | | selectedSkill | AiSkill / null | 当前激活的技能 | | selectSkill | (skill) => void | 选中技能;同时自动重置草稿中的知识与自定义参数 | | params | Object | 发送参数对象,直接赋值即随消息提交(与 render.params 同一对象) |

技能列表取自响应式全局配置:组件挂载后再调用一次 configureAi({ skills })(如按登录用户权限切换技能)可即时生效,无需重新挂载组件。

自定义欢迎页标题、问候语与侧边栏头部(#welcome-header / #welcome-greeting / #sidebar-header 插槽)

三块品牌区域均开放给宿主自定义,Agent / AiChat 上使用(组件内部逐层透传,仅桌面布局生效):

  • #welcome-header:欢迎页(无会话空态)标题区内容。组件内置标题已移除,不传该插槽时标题区为空白(仅保留原有间距布局),宿主自行渲染品牌标题、横幅图等
  • #welcome-greeting:欢迎页推荐卡片顶部的问候语区(原「路安安」头像 + 文案)。组件内置内容已移除,不传该插槽时整块不渲染(不会留出空白条)
  • #sidebar-header:侧边栏顶部区域(默认为空),可放自定义 logo、标题栏等;小窗模式(500px / 1000px 双屏)侧边栏整体隐藏,插槽不渲染
<template>
  <Agent>
    <!-- 欢迎页标题:替换内置「数字赋能监控」标题图 -->
    <template #welcome-header>
      <img src="./my-banner.png" alt="产品标题" />
    </template>
    <!-- 推荐卡片顶部问候语:容器(高度 64px、相对定位)由组件提供,内部内容自行编写 -->
    <template #welcome-greeting>
      <img
        src="./my-bot.png"
        alt="bot"
        class="w-[84px] h-[84px] absolute top-[-12px] left-[18px]"
      />
      <span class="text-[18px] text-[#333333] font-bold pl-[94px]">
        Hi!我是路安安。我可以为您解答高速公路行业的政策及业务规范!
      </span>
    </template>
    <!-- 侧边栏头部:渲染在「新对话 / 收藏夹」菜单上方 -->
    <template #sidebar-header>
      <div class="my-sidebar-logo">AI 助手</div>
    </template>
  </Agent>
</template>

三个插槽均为无作用域插槽,直接传模板内容即可;AiChat(全屏嵌入形态)上用法相同。 #welcome-greeting 的外层容器由组件提供(px-6 py-4 flex items-center relative h-[64px] overflow-visible),头像若需要悬浮出卡片上沿,按示例加 absolute top-[-12px] left-[18px] 即可。

三、组件使用

1. Agent(AI 悬浮助手:右下角小人 + 对话弹窗)

组件渲染常驻右下角的"知识 IP 小人",点击小人弹出对话窗口(整组件 Teleport 到 body,宿主任意祖先元素的 transform/filter 不影响定位):

  • 弹窗锚定视口右下角,以圆弧扩散动画展开,初始为视口的 85% × 85%
  • 弹窗左缘 / 顶缘 / 左上角可拖拽调整大小(右、下边缘固定)
  • AI 页面随组件直接挂载(挂载时自动拉取用户信息);收起仅隐藏不销毁,会话上下文保留,后台流式回答继续且滚动位置不丢
  • AI 回复结束后收起弹窗时,小人带完成提示气泡跳出展示,约 6.5 秒后自动消失
  • 小人本体可上下拖动:拖到左/右自动贴边,松手带纵向惯性;位置写入 localStorage(贴边方向 + 纵向相对比例,换分辨率后仍合理),双击复位;位移小于 4px 仍按点击处理,不会误触打开弹窗
  • 小人聚焦后支持键盘操作:上下方向键微调、左右方向键切换贴边、Home 复位
<script setup>
import { Agent } from 'leatop-ai';
</script>

<template>
  <Agent />
</template>

| Prop | 类型 | 默认 | 说明 | | ------------- | ---------------- | ------------------ | ------------------------------------------------------------------------------- | | width | String | 85vw | 弹窗初始宽度(css 值) | | height | String | 85vh | 弹窗初始高度(css 值) | | min-width | Number | 850 | 拖拽允许的最小宽度(px) | | min-height | Number | 500 | 拖拽允许的最小高度(px) | | right | String | 20px | 弹窗距视口右侧的距离(css 值) | | bottom | String | 20px | 弹窗距视口底部的距离(css 值) | | name | String | Hi!我是路安安 | 小人首次问候气泡文案 | | finish-text | String | 对话已完成,需要我时随时点我哦~ | 对话完成提示语,传空字符串则不提示 | | messages | BubbleMessage[] | [] | 小人气泡消息列表,元素结构 { id, text, type? },type 可选 speaking / listening / none | | draggable | Boolean | true | 小人是否可上下拖动;传 false 时位置固定,点击小人仍可打开弹窗 | | drag-limit | Object | {} | 小人可拖动范围:距视口四边的内边距(px),如 { left: 16, right: 16, bottom: 16 };负值可让形象压出边缘 | | default-open | Boolean | false | 是否初始自动打开对话弹窗(挂载后以圆弧扩散动画展开,无需点击小人) | | actions | Array / Function | null | 自定义头部动作项,详见「2.1 头部操作区自定义(AiChat / Agent 通用)」;内置收起按钮为 key: "close" 的动作,可覆盖/删除 | | validate-send | Function | null | 发送前校验(透传给内部 AiChat),优先级高于全局 configureAi({ validateSend }),详见「2.2 发送前校验(validateSend)」 |

| 插槽 | 作用域 | 说明 | | --- | --- | --- | | #skills | { skills, selectedSkill, selectSkill, params } | 接管技能区(上传按钮保留),详见「技能区整体接管(#skills 插槽)」 | | #actions | { actions, defaults, context, trigger } | 接管头部动作区,详见「4)示例:#actions 插槽整块接管」 | | #welcome-header | 无 | 欢迎页标题区,不传则为空白,详见「自定义欢迎页标题、问候语与侧边栏头部」 | | #welcome-greeting | 无 | 欢迎页推荐卡片顶部问候语,不传则整块不渲染,详见同上 | | #sidebar-header | 无 | 侧边栏顶部区域,小窗模式不渲染,详见同上 |

| 事件 | 参数 | 说明 | | --------------- | -- | ----------------------------------------- | | setting-click | 无 | 用户点击侧边栏"设置"按钮时触发,由宿主自行处理(跳知识库页、打开抽屉等) |

实例方法(通过组件 ref 调用):

| 方法 | 说明 | | --------- | -------------------------------------- | | open() | 打开对话弹窗 | | close() | 收起对话弹窗;若存在待展示的完成提示,会跳出小人并显示提示气泡 | | refreshQuestions() | 重新请求接口刷新欢迎页「常见问题」列表(不是从已拉取数据里「换一批」),无返回值 |

<script setup>
import { ref } from 'vue';
import { Agent } from 'leatop-ai';

const agentRef = ref();

// 手动控制弹窗显隐(如由宿主自己的按钮触发)
const openAgent = () => agentRef.value?.open();
const closeAgent = () => agentRef.value?.close();
</script>

<template>
  <!-- 传 default-open 则挂载后自动展开对话;不传则保持右下角小人入口 -->
  <Agent
    ref="agentRef"
    default-open
    name="Hi!我是小智"
    finish-text="本轮回完啦~"
    width="60vw"
    height="70vh"
    @setting-click="onSettingClick"
  />
</template>

1.1.0:Agent 已从全屏页面升级为悬浮入口,原 is-compact / is-double-screen / initial-tab / initial-event-id props 不再在 Agent 上暴露;如需独立的全屏对话页形态,请使用下方 AiChat 组件。

2. AiChat(AI 对话页面)

内部完整 AI 对话页(历史会话、收藏、知识库引用、全屏等能力齐全),宿主可将其嵌入自己的容器(全屏页、抽屉、自定义弹窗等):

<script setup>
import { AiChat } from 'leatop-ai';
</script>

<template>
  <AiChat @setting-click="onSettingClick" @chat-finish="onChatFinish" />
</template>

| 事件 | 参数 | 说明 | | --------------- | -- | ----------------------------------------- | | setting-click | 无 | 用户点击侧边栏"设置"按钮时触发,由宿主自行处理(跳知识库页、打开抽屉等) | | chat-finish | 无 | 任一会话的 AI 回复流结束时触发(可据此播提示音、发通知等) |

| Prop | 类型 | 默认 | 说明 | | --------- | ---------------- | ------ | --------------------------------- | | actions | Array / Function | null | 自定义头部动作项,详见「2.1 头部操作区自定义(AiChat / Agent 通用)」 | | validate-send | Function | null | 发送前校验,优先级高于全局 configureAi({ validateSend }),详见「2.2 发送前校验(validateSend)」 |

| 插槽 | 作用域 | 说明 | | --- | --- | --- | | #skills | { skills, selectedSkill, selectSkill, params } | 接管技能区(上传按钮保留),详见「技能区整体接管(#skills 插槽)」 | | #actions | { actions, defaults, context, trigger } | 接管头部动作区,详见「4)示例:#actions 插槽整块接管」 | | #welcome-header | 无 | 欢迎页标题区,不传则为空白,详见「自定义欢迎页标题、问候语与侧边栏头部」 | | #welcome-greeting | 无 | 欢迎页推荐卡片顶部问候语,不传则整块不渲染,详见同上 | | #sidebar-header | 无 | 侧边栏顶部区域,小窗模式不渲染,详见同上 |

实例方法(通过组件 ref 调用):

| 方法 | 说明 | | --- | --- | | refreshQuestions() | 重新请求接口刷新欢迎页「常见问题」列表(不是从已拉取数据里「换一批」),无返回值 |

<script setup>
import { ref } from 'vue';
import { AiChat } from 'leatop-ai';

const chatRef = ref();

// 后台常见问题配置更新后,主动拉取最新列表
const reloadQuestions = () => chatRef.value?.refreshQuestions();
</script>

<template>
  <AiChat ref="chatRef" />
</template>

2.1 头部操作区自定义(AiChat / Agent 通用)

头部右侧的动作区(新对话 / 收藏 / 全屏)由配置驱动,宿主可整体替换、按状态改写,或直接接管渲染。

默认动作:new-chat(紧凑态显示)、collect / uncollect(收藏与取消收藏互斥)、toggle-fullscreen(受 uiConfig.showFullscreenButton 控制)。

1)actions 的两种形态

<!-- 形态一:数组 = 完整列表(只渲染这些,缺省 visible 为 true) -->
<AiChat :actions="[{ key: 'toggle-fullscreen', icon: 'ui-fullscreen', title: '全屏' }]" />

<!-- 形态二:函数 = 拿到「默认动作 + 上下文」,返回什么就渲染什么(不自动合并) -->
<AiChat :actions="resolveActions" />

函数第二个参数 context(只读):

| 字段 | 类型 | 说明 | | --- | --- | --- | | tab | String | 当前视图:newchat(新对话)/ history-detail(会话详情)/ history(历史列表)/ favorites(收藏) | | isSession | Boolean | 当前会话是否已有消息(chatStore.hasChat);新对话空态为 false | | isCollected | Boolean | 当前会话是否已收藏 | | compact / minimal | Boolean | 是否紧凑态 / 极简态 | | isFullscreen | Boolean | 是否浏览器全屏 | | isSmallWindow | Boolean | 是否小窗模式 | | showFullscreenButton | Boolean | 是否启用全屏按钮 |

2)动作项字段

| 字段 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | key | String | 是 | 唯一标识,用于渲染 key、去重与事件 type 兜底 | | title | String | 否 | 原生 title + aria-label | | icon | String | 否 | 包内雪碧图名称(如 newchat、star、ui-fullscreen) | | iconSize | String | 否 | 图标尺寸,如 24px / 32px | | iconColor | String | 否 | 图标填充色,缺省跟随文字色 | | component | Component | 否 | 自定义渲染组件,优先级高于 icon | | componentProps | Object | 否 | 透传给 component 的 props | | visible | Boolean | 否 | 是否渲染,缺省 true;传 false 即隐藏/删除该项 | | disabled | Boolean | 否 | 禁用态,不触发点击 | | active | Boolean | 否 | 激活态高亮 | | badge | String / Number | 否 | 右上角角标,0 与空串不显示 | | order | Number | 否 | 排序权重,数字小的在前;不传则排在已设 order 的项之后 | | onClick | Function | 否 | 点击回调,入参 { key, action, context, event } | | event | String | 否 | 兼容字段:组件内部动作分发使用的 type,缺省取 key;宿主自定义动作直接用 onClick 即可 |

3)示例:追加按钮 / 按会话状态改写

import type { HeaderActionsResolver } from 'leatop-ai';

const resolveActions: HeaderActionsResolver = (defaults, context) => [
  // 保留全部默认动作,仅改写全屏按钮文案
  ...defaults.map((item) =>
    item.key === 'toggle-fullscreen' ? { ...item, title: '全屏模式' } : item,
  ),
  // 有会话时才出现「分享」按钮
  ...(context.isSession
    ? [{
        key: 'share',
        icon: 'share',
        iconSize: '24px',
        title: '分享对话',
        onClick: () => openShareDialog(),
      }]
    : []),
];
<AiChat :actions="resolveActions" />
<Agent :actions="resolveActions" />

4)示例:#actions 插槽整块接管

插槽作用于 AiChat / Agent,提供后默认渲染整块由宿主接管(容器间距、ml-auto 仍由组件提供):

| 参数 | 类型 | 说明 | | --- | --- | --- | | actions | HeaderAction[] | 归一化后的最终动作列表(与默认渲染同源) | | defaults | HeaderAction[] | 内置默认动作,便于插槽内再判断 | | context | Object | 同上文上下文 | | trigger | (action, event?) => void | 统一点击入口:执行 onClick,并交给组件内部动作分发(宿主不必自己转发) |

<script setup>
import { HeaderActionButton } from 'leatop-ai';

const onExport = () => exportConversation();
</script>

<template>
  <AiChat :actions="resolveActions">
    <template #actions="{ actions, trigger }">
      <!-- 用 HeaderActionButton 复用内置按钮样式 -->
      <HeaderActionButton
        v-for="item in actions"
        :key="item.key"
        :action="item"
        @click="trigger(item, $event)"
      />
      <!-- 插槽内可放任意内容,样式需宿主自己写 -->
      <button type="button" class="my-export-btn" @click="onExport">导出</button>
    </template>
  </AiChat>
</template>

插槽内容在宿主作用域编译,组件库的 scoped 样式不会作用到槽内元素;需要与内置按钮一致的外观时直接使用 HeaderActionButton。

5)示例:Agent 的「收起」按钮也可控

Agent 悬浮窗的内置收起按钮是 key 为 "close" 的普通动作项,随 actions 一并交给宿主:

// 删除收起按钮(函数形态直接过滤)
const noClose: HeaderActionsResolver = (defaults) =>
  defaults.filter((item) => item.key !== 'close');

// 只改图标/文案,保留默认的收起回调(未覆盖的字段会继承默认项)
const renameClose: HeaderActionsResolver = (defaults) =>
  defaults.map((item) => (item.key === 'close' ? { ...item, icon: 'minus', title: '最小化' } : item));

// 数组形态下同 key 覆盖默认项,visible: false 即删除
const onlyNewChat = [
  { key: 'new-chat', icon: 'newchat', iconSize: '24px', title: '新对话' },
  { key: 'close', visible: false },
];

数组形态是「完整列表」,只写需要的项;close 仅 Agent 有。宿主传入 actions 后,AiChat 双屏模式下「只显示新对话」的内部规则让位。

2.2 发送前校验(validateSend)(AiChat / Agent 通用)

宿主可插入业务校验(知识库必选、字数上限、敏感词、权限/额度、二次确认)。校验在参数组装完成、真正发送之前执行;失败时不发送,输入内容与附件都保留。

1)两种使用位置

// 全局:所有 AiChat / Agent 实例生效
configureAi({
  validateSend: (payload, context) => {
    // 校验逻辑
  },
});
<!-- 组件级:只对当前实例生效,优先级高于全局配置 -->
<AiChat :validate-send="validateFn" />
<Agent :validate-send="validateFn" />

2)入参

payload:与 @send 事件拿到的内容完全一致(不含技能对象,技能见 context.skill)

| 字段 | 类型 | 说明 | | --- | --- | --- | | text | String | 用户输入(已 trim) | | type | String | 当前技能 type,未选技能时为空串 | | extraParams | Object | 最终请求参数(知识库 + 技能 params + getExtraParams 合并结果) | | attachments | AiSendAttachment[] | 已上传成功的附件(含 name / format / size 等) |

context:payload 之外的附加信息(技能对象、知识库)

| 字段 | 类型 | 说明 | | --- | --- | --- | | skill | AiSkill / null | 当前选中的技能 | | knowledgeBaseIds | String[] | 选中的知识库 identifier;未选/全部知识库时为空数组 |

3)返回值

| 返回 | 行为 | | --- | --- | | true / undefined | 放行,继续发送 | | false / '' | 静默阻断(宿主自己弹窗时用) | | '提示文案' | 阻断并 ElMessage.warning(文案) | | Promise<...> | 异步等待;期间发送按钮进入等待态,输入与附件保留 | | 抛异常 | 仅 console.log,静默阻断 |

4)示例:同步校验(知识库必选 + 字数上限)

configureAi({
  validateSend: (payload, context) => {
    // 知识问答必须先选具体知识库
    if (payload.type === '1' && !context.knowledgeBaseIds.length) {
      return '请先选择知识库';
    }
    // 字数上限
    if (payload.text.length > 2000) {
      return '内容过长,请精简到 2000 字以内';
    }
    // 既没文字也没附件(内部已拦,这里演示兜底写法)
    if (!payload.text && !payload.attachments.length) {
      return '请输入内容或上传附件';
    }
    // 不返回 = 放行
  },
});

5)示例:异步校验(权限 / 额度)

configureAi({
  validateSend: async (payload) => {
    const { allowed, message } = await checkQuota(payload.type);
    if (!allowed) return message || '今日额度已用完';
    // await 期间发送按钮进入等待态,用户无法重复提交
  },
});

6)示例:组件级局部规则

<script setup>
// 只对当前页面生效,优先级高于全局配置
const validateForPage = (payload) =>
  payload.text.length <= 500 || '单条消息不超过 500 字';
</script>

<template>
  <AiChat :validate-send="validateForPage" />
  <Agent :validate-send="validateForPage" />
</template>

7)示例:静默阻断(宿主自己弹确认框)

configureAi({
  validateSend: (payload) => {
    if (payload.extraParams.needConfirm) {
      openConfirmDialog(payload); // 宿主自己的弹窗
      return false; // 静默阻断,组件不再 toast
    }
  },
});

8)规则说明

  • 只做「放行 / 阻断」,不支持改写 payload;需要追加参数请继续用 getExtraParams;
  • 优先级:AiChat / Agent 的 validate-send prop > configureAi({ validateSend });
  • 附件在校验前已经上传完成,validateSend 里拦附件只能阻止发送,不会撤销已上传文件;
  • 不配置时行为与之前完全一致;空内容、上传中/失败、生成中、20MB/50MB 限制等内部约束仍由组件负责,不下放给宿主。

3. KnowledgeBase(知识库,原 /confluence 页面)

<template>
  <KnowledgeBase />
</template>

无需 props,接口配置走全局 configureAi。

4. Question(常见问题,原 /question 页面)

<template>
  <Question />
</template>

5. 报表文件下载卡片(AiChat / Agent 内部行为)

后端 /ai/stream/chat 返回 excel-ready 事件(字段 attachmentId / filename / downloadUrl)时,助手消息会自动展示文件下载卡片,点击优先走 downloadUrl 直链,无直链时回退 attachmentId 下载接口。宿主管辖范围内无需额外配置。

历史回显依赖后端历史消息带回 file 字段(含 attachmentId)且消息 type 为 3 / 4;缺失时刷新页面后卡片不显示,需后端补齐落库字段。

四、样式引入

// 宿主 main.ts
import 'leatop-ai/dist/leatop-ai.css';

包含组件样式、tailwind 工具类、SvgIcon 雪碧图,以及 Element Plus 命令式组件 (ElMessage / ElMessageBox / ElNotification)的结构样式——宿主无需再单独引入 EP 样式, 但 element-plus 包本身仍需安装(peer 依赖)。若宿主已有全局样式冲突 (preflight 重置),可在接入评审时协商加样式前缀隔离。

Element Plus 中文文案:Question / KnowledgeBase 内部已用 ElConfigProvider(zh-cn)包裹, 分页「共 x 条」、表格空态「暂无数据」等内置文案为中文,与宿主是否配置 locale 无关。 Agent / AiChat 不含表格与分页,宿主若希望整站统一中文,按常规在应用入口 app.use(ElementPlus, { locale: zhCn }) 配置即可。

五、版本更新记录

1.5.7(2026-10-23)

修复生成中「停止生成」按钮被置灰(视觉修复,无接口变更):

  • 触发条件:宿主配置 uiConfig.showVoiceButton: false(隐藏麦克风)时,生成中输入框已清空、无附件,按钮误命中「空内容禁用发送」规则进入 disabled,被 grayscale(1) + opacity: 0.5 滤镜变成灰色
  • 修复方式:loading(生成中)优先于所有禁用条件,停止按钮始终为蓝色可点击态;非生成时仍按原规则处理空内容、附件上传中/失败、语音识别中
  • 影响范围:AiChat / Agent / 小屏 FixedScreen 三处输入区共用同一组件,行为一致

1.5.6(2026-10-23)

新增刷新常见问题实例方法(无接口变更,宿主按需调用):

  • AiChat / Agent 新增实例方法 refreshQuestions():重新请求 getQuestionList 刷新欢迎页「常见问题」列表,与卡片右上角「换一批」的本地翻页不同,调用后重置到第一页并拿到后台最新配置;无返回值
  • 调用链为 Agent → AiChat → MainContent → WelcomeScreen,宿主通过组件 ref 调用即可,无需改模板或配置
  • 仅在新对话/会话详情视图(欢迎页所在视图)下生效:其它视图下调用会静默跳过,切回欢迎页不会自动补刷

1.5.5(2026-10-22)

数字人拖拽与会话标识透传(无接口变更,宿主无需改动):

  • 新增数字人(Agent 右下角小人)上下拖拽:拖到左/右自动贴边、松手带纵向惯性;位置写入 localStorage(保存贴边方向 + 纵向相对比例,换分辨率后位置依旧合理),双击复位;位移小于 4px 仍视为点击,不会误触打开弹窗
  • 拖拽细节:横向位置锁死在当前贴边(拖拽只改纵坐标)、位置用 translate3d 驱动不触发布局、拖拽期间铺满视口的透明遮罩避免指针划过面板 iframe 丢事件、拖拽吞掉尾随 click;失焦/焦点在小人上时支持上下方向键微调、左右方向键切换贴边、Home 复位
  • Agent 新增 props draggable(默认 true)与 drag-limit(距视口四边的内边距,如 { left: 16, right: 16, bottom: 16 },负值可压出边缘):关闭拖拽后小人位置固定、点击行为不变;drag-limit 运行时可响应式调整,越界会自动夹回安全区
  • 流式会话标识透传:SSE 回传 chatRecordId 时组件库自动记录并回填会话,下一轮 /ai/stream/chat 请求同时携带 sessionId 与 chatRecordId(两者独立取值、互不替代,缺失时传空串),便于后端按历史记录续聊

1.5.4(2026-10-21)

报表下载卡片与 Element Plus 中文文案(无接口变更,宿主无需改动):

  • 报表生成结束(后端 excel-ready 事件)后自动展示文件下载卡片:事件携带 attachmentId / filename / downloadUrl,组件库写入消息并渲染预览卡片;点击卡片优先用 downloadUrl 直链下载,无直链时回退附件下载接口
  • 报表卡片历史回显兼容 filename / downloadUrl 字段命名(此前仅识别 fileName / cdpUrl,导致刷新后卡片标题为空、直链丢失),消息 type 同时兼容字符串形态("3" / "4")
  • Question / KnowledgeBase 内部用 ElConfigProvider(zh-cn) 包裹:表格空态、分页「共 x 条」等 Element Plus 内置文案由英文改为中文,不再受宿主全局 locale 影响

1.5.1(2026-10-20)

样式修复(视觉变更,宿主需做一次回归):

  • 修复收藏按钮「已收藏」态图标不变蓝:iconColor 改为通过 CSS color 生效(雪碧图 path 自带 fill="currentColor",原 <use fill> 被覆盖)
  • 修复组件内 border / border-[#xxx] 与 hover: 边框全部失效:reset 改用 :where() 降低特异性,不再覆盖 tailwind 边框工具类;边框恢复显示后请回归相关页面

1.5.0(2026-10-20)

头部操作区改为配置驱动,宿主可自定义(含破坏性变更,宿主需同步调整):

  • 新增 actions prop(Agent / AiChat):支持数组(完整列表)与函数 (defaults, context) => actions(返回值即最终结果,不与默认合并);context 提供 tab / isSession / isCollected / compact / minimal / isFullscreen / isSmallWindow / showFullscreenButton——tab 为当前视图(newchat / history-detail / history / favorites),isSession 表示当前会话已有消息(新对话空态为 false)
  • 动作项新增 onClick / disabled / active / badge / order / componentProps:点击统一走 onClick;内置动作仍通过 event 字段走组件内部事件分发(兼容原有行为)
  • 新增 #actions 作用域插槽(Agent / AiChat):载荷 { actions, defaults, context, trigger },可直接接管整块动作区渲染
  • 新增包入口导出 HeaderActionButton 及类型 HeaderAction / HeaderActionContext / HeaderActionClickPayload / HeaderActions / HeaderActionsResolver / HeaderActionsSlotProps:插槽内渲染动作时可直接复用内置按钮样式(插槽内容不带组件的 scoped 样式)
  • Agent 内置收起按钮改为 key: "close" 的普通动作项,随 actions 交给宿主——可改图标/文案/回调,也可过滤删除
  • 删除 #append-actions 与 AiChat 的 header-append 插槽(破坏性变更):原先「在动作区末尾追加按钮」的能力统一由 #actions 承接;宿主迁移方式为在 #actions 中渲染 actions + 自定义按钮(见「2.1 头部操作区自定义」)
  • 头部动作统一渲染为 <button>(收藏/取消收藏由 div 改为 button),按钮仅清除浏览器默认样式、尺寸随图标,视觉与原先一致;补充键盘焦点环、禁用态,支持 disabled / badge
  • 动作区尺寸收敛:容器去掉固定 80px 高度与重复色值,图标统一 24 / 32 两档(原小窗 34px 取消),紧凑态按钮不再强制 36×36
  • AiChat 双屏仍默认「只显示新对话」;宿主显式传入 actions 时以宿主配置为准
  • 新增 configureAi({ validateSend }) 与 Agent / AiChat 的 validate-send prop:发送前自定义校验,在参数组装完成、真正发送之前执行;返回 false 静默阻断、返回字符串阻断并提示、返回 true/undefined 放行,支持 Promise(校验期间按钮进入等待态、输入与附件保留);异常仅 console.log 静默阻断。组件级 prop 优先于全局配置
  • 新增包入口导出类型:AiSendPayload / AiSendAttachment / AiSendContext / AiValidateSendResult / AiValidateSend
  • 说明:空内容、附件上传中/失败、生成中、文件大小限制等内部约束不交给宿主校验,validateSend 只承接业务规则(知识库必选、长度、敏感词、权限额度等)

1.4.2(2026-10-19)

输入框占位文案与欢迎页修复:

  • 新增 uiConfig.inputPlaceholder:自定义未选中技能时的输入框默认占位文案;优先级为「技能 placeholder → uiConfig.inputPlaceholder → 包内默认『发消息或者按住空格说话...』」
  • 修复 #welcome-header 插槽不渲染:1.4.1 中欢迎页标题区是一个空的占位 div,<slot name="welcome-header"> 未被输出,宿主传入的标题内容实际不显示;现已接上插槽
  • 欢迎页间距调整:标题区上边距 130px,标题区与推荐卡片之间保留弹性占位(206px),推荐卡片间距 40px

1.4.1(2026-10-19)

问候语插槽与弹窗样式内置(含视觉变更,宿主需确认):

  • 新增 #welcome-greeting 插槽(Agent / AiChat,逐层透传):欢迎页推荐卡片顶部的问候语区(原「路安安」头像 + 文案)交由宿主渲染;外层容器(64px 高 / relative / px-6 py-4)仍由组件提供,宿主只写内部内容
  • 移除欢迎页内置问候语(视觉变更):不传 #welcome-greeting 时整块不渲染(不留空白条),推荐卡片顶部直接接常见问题区;需要原有问候语请按 README 插槽示例自行渲染
  • 弹窗/提示样式随包内置:补入 Element Plus 命令式组件样式(ElMessage / ElMessageBox / ElNotification)。三者均为 JS 调用,unplugin-vue-components 的按需样式注入覆盖不到——此前宿主自身若未引入 message-box 等样式,确认框会退化为无遮罩、无居中、无动画的普通 div。现由 dist/leatop-ai.css 直接提供
  • 接入调整:宿主如曾为确认框手动 import 'element-plus/es/components/message-box/style/css',可删除(重复引入无害);element-plus 包本身仍是 peer 依赖,必须安装
  • 构建调整:样式类模块(*.css 与 element-plus/.../style/css)不再 external,统一打进产物 css;@pureadmin/table 样式随之进入产物,宿主无需额外引入
  • 产物体积:leatop-ai.css 约 666KB → 1135KB(gzip 约 696KB),增量来自 EP base 变量表、三个命令式组件结构样式、pureadmin table 样式与内联背景图
  • 新增 uiConfig.showVoiceButton(默认 true):传 false 隐藏输入区语音输入(麦克风)按钮,空输入时该位置显示发送按钮并呈现禁用态(0.5 透明度 + 禁止光标);读响应式全局配置,挂载后再调 configureAi 即时生效
  • 新增 uiConfig.assistantAvatar:AI 消息头像支持外部 URL(http(s) 链接 / base64),传了用外部图片,不传或为空沿用包内默认头像;响应式配置,可运行时切换

1.3.0(2026-10-16)

技能自定义渲染能力(含破坏性变更,宿主需同步调整):

  • 新增技能字段 render:技能激活后在技能标识后面渲染宿主传入的自定义组件,支持组件定义(render: A)与工厂函数(render: ({ skill, params }) => A,可返回 defineAsyncComponent)两种写法
  • render 组件双向参数:组件通过 props 接收 { skill, params },params 为草稿级响应式对象,组件内直接赋值即随消息发送(自动并入 /ai/stream/chat 请求体);切换技能/清空输入时由组件库重置,发送后保留,小窗与主输入区切换自动互通
  • 新增 #skills 插槽(Agent / AiChat):技能区整块交由宿主实现(上传按钮保留),插槽作用域提供 skills / selectedSkill / selectSkill / params
  • 新增包入口导出:useChatDraft(宿主 #skills 自定义时读写输入草稿)、类型 AiSkillRenderContext
  • 移除技能配置项 knowledgeBase(破坏性变更):知识库选择下拉不再作为宿主配置暴露,内置「知识问答(type 1)」的下拉保留为包内内部行为不受影响;自定义技能需要选择类交互请用 render 实现
  • 发送参数合并优先级固定为:knowledgeIds(内置知识库)→ render.params → getExtraParams(宿主函数最高,同名覆盖)
  • 新增 uiConfig.showUploadButton(默认 true):传 false 隐藏输入区上传文件按钮(+);与 #skills 插槽接管、响应式配置即时生效机制兼容
  • 新增 #welcome-header / #sidebar-header 插槽(Agent / AiChat,逐层透传):自定义欢迎页标题区与侧边栏顶部区域;小窗模式侧边栏隐藏,#sidebar-header 不渲染
  • 移除欢迎页内置标题(破坏性变更):原「数字赋能监控,智慧保障畅行」标题图不再内置,不传 #welcome-header 时欢迎页标题区为空白(保留间距布局),宿主通过插槽自行渲染品牌内容

1.2.5(2026-10-16)

token 头与用户信息获取可自定义:

  • 新增 configureAi({ tokenKey }):请求携带 token 的 header key 可配置,默认 cdp-token,宿主可按后端约定改为其他 key(如 Authorization);axios 请求、附件上传、SSE 流式请求三处统一生效
  • 新增 configureAi({ getUserInfo }):宿主传入异步(Promise)函数自行获取用户信息,resolve 组件库所需字段即可(如 { account, userName, avatar }),返回值直接合并进组件库 user store;配置后组件库不再请求 /system/user/curUser,且不强制要求先有 token(登录态由宿主函数自行负责)。不传保持原行为:组件挂载时按 token 自动调用 /system/user/curUser

1.2.4(2026-10-15)

Agent 弹窗显隐控制:

  • 新增 prop default-open(默认 false):传 true 时组件挂载后自动打开对话弹窗(带圆弧扩散动画),无需点击右下角小人
  • 新增实例方法 open() / close():宿主通过 ref 主动控制弹窗显隐;close() 与界面收起按钮行为一致——存在待展示的完成提示时,跳出小人并显示提示气泡
  • 自动打开在 onMounted 中经 nextTick 触发:先以隐藏态提交首帧、再切到打开态,保证 clip-path 扩散动画有起始帧(若直接就绪于打开态会直接闪出、无动画)

1.2.3(2026-10-15)

技能与请求参数自定义:

  • 新增 configureAi({ skills }):宿主可裁剪或自定义输入区技能列表——不传为内置「知识问答(1) + 智能问数(2)」;字符串数组按内置技能筛选(如 ['2'] 只留智能问数,即隐藏知识问答);对象数组可覆盖内置技能字段或新增自定义技能(type 命中内置时未填字段沿用内置值)
  • 技能字段扩展:新增 iconUrl(自定义图标图片,渲染优先于包内雪碧图 icon)、knowledgeBase(是否展示知识库选择下拉,内置仅 type 1 开启)
  • 新增 configureAi({ getExtraParams }):发送消息前按当前技能注入额外请求参数(自定义技能所需的业务参数),同名参数以宿主返回值为准
  • 全局配置改用 reactive 包装:组件挂载后再次调用 configureAi 修改技能列表可即时生效
  • 修复技能识别硬编码:知识库下拉的判断由技能名(name === '知识问答')改为技能声明(knowledgeBase),宿主重命名内置技能后不再丢失知识库选择

1.2.1(2026-10-15)

宿主接入 1.2.0 后的三项修复:

  • 修复宿主 TS 报 7016"找不到模块 leatop-ai 的声明文件":types 入口原指向不存在的 dist/types/index.d.ts,已修正为实际产物 dist/index.d.ts,宿主更新后即可获得组件 props/事件类型提示
  • 修复 Teleport 弹层样式退化:下拉/气泡/MessageBox/图片预览等弹层渲染到 body 后脱离 .leatop-ai-root 作用域,弹层内边框、滚动条样式异常;已通过统一作用域清单覆盖各弹层
  • 修复悬浮弹窗内下拉面板点不开:Agent 悬浮窗口层级(原 2100)高于 Element Plus 弹层基础层级(2000),弹窗内的下拉/tooltip/确认框等被窗口盖住;窗口层级下调至 2000

1.2.0(2026-10-15)

AiChat 组件导出与类型声明自动化:

  • 新增 AiChat 组件导出:内部完整 AI 对话页(历史会话/收藏/知识库引用/全屏),宿主可将其嵌入自有容器(全屏页/抽屉/自定义弹窗);事件 setting-click(设置按钮点击)/ chat-finish(AI 回复流结束)
  • 类型声明改为构建时自动生成(vite-plugin-dts + vue-tsc):dist/types/index.d.ts 由各组件 defineProps / defineEmits 直接转换产出,组件 props/事件变更时类型自动同步,不再手写维护 types/leatop-ai.d.ts
  • 依赖调整:vue-tsc 升级 1.8 → 2.2,新增 devDependency vite-plugin-dts

全局样式防污染重构(修复组件库 css 污染宿主整站样式):

  • 移除原管理后台残留的全局样式:删除 sidebar/dark/cdp/transition/variables/mixin 等死样式文件,theme.scss 从 2370 行裁剪至约 110 行,仅保留 leatop-ai-dialog / leatop-ai-messagebox / pure-table 三块已作用域限定的主题规则——清除 .theme-dark / .theme-blue 换肤规则对宿主主题的覆盖,以及大量无作用域 .el-button / .el-input / .el-table 等规则对宿主 element-plus 外观的影响
  • reset 与滚动条样式命名空间化:全局重置(* border/box-sizing)与 :root 滚动条规则统一收敛到 .leatop-ai-root 根类,Agent/AiChat/KnowledgeBase/Question 组件根节点自动挂载,不再修改宿主 html/body/#app 与整站滚动条外观
  • 收紧裸选择器:知识库下拉高亮 .el-dropdown-menu__item.is-active-knowledge 限定到 .custom-plus-dropdown 弹层作用域,避免命中宿主下拉菜单项

1.1.1(2026-10-14)

修复生产构建小人裂图:

  • 修复打包后知识 IP 小人动效图全部不显示:APNG 重播机制由"src 追加 ?t= 时间戳"改为 :key 重建 img 元素——生产构建图片被 base64 内联为 dataURL,追加 query 会破坏数据格式导致裂图(dev 模式为普通 URL 故无此问题)

1.1.0(2026-10-14)

Agent 悬浮化重构:

  • Agent 升级为悬浮入口:默认渲染右下角知识 IP 小人,点击弹出对话窗口(整体 Teleport 到 body);内部 AI 全屏页不再单独导出,原 is-compact / is-double-screen / initial-tab / initial-event-id props 移除
  • 对话弹窗锚定视口右下角,初始 85vw × 85vh,左缘/顶缘/左上角可拖拽调整大小(右、下边缘固定);出现/收起为右下角圆弧扩散动画
  • 弹窗显隐采用 visibility 而非 display:none:收起期间后台流式回答的滚动位置计算不失效,重新打开时消息区自动位于最新底部
  • 新增 props:width / height(弹窗初始尺寸)、min-width / min-height(拖拽下限)、right / bottom(锚定位置)、name(小人问候文案)、finish-text(对话完成提示语,空串关闭)、messages(小人气泡消息列表)
  • AI 回复流结束后收起弹窗,小人带完成提示气泡跳出展示,约 6.5s 自动消失;对话中收起时小人隐藏,避免与弹窗重叠

1.0.7(2026-10-14)

弹窗主题补全:

  • ElMessageBox 确认框(删除历史/重命名/删除收藏/批量删除等 8 处)统一挂 leatop-ai-messagebox class,主题规则同步作用域化并提权——此前 JS 调用的确认框未走组件库主题,被宿主全局样式接管
  • dialog 与 message-box 的 header 显式清除 border-bottom(!important),修复宿主全局分隔线规则导致的标题栏下边框残留

1.0.6(2026-10-14)

弹窗与图标宿主兼容修复:

  • 修复宿主端 SVG 图标丢失:组件库雪碧图改用独立 DOM 容器 id(__leatop_ai_svg_icons_dom__),避免与宿主(同用 vite-plugin-svg-icons)的默认 id 冲突——同 id 时插件会整体跳过注入,导致后注册方图标全部 404
  • 弹窗样式作用域化:组件库全部 el-dialog 统一挂 leatop-ai-dialog class,弹窗主题规则限定该作用域并提权——弹窗 append-to-body 后不再被宿主全局弹窗样式覆盖(修复 header 背景丢失、背景图未覆盖到标题栏的问题),同时不污染宿主整站弹窗外观
  • 弹窗统一 append-to-body:遮罩渲染到 body 层,修复宿主内容区存在 transform/filter 祖先时遮罩降级定位、无法全屏覆盖的问题
  • 移除 /ai/stream/tongwen 技能判断接口:发送消息不再单独预判技能类型,外部有 type 时随 /ai/stream/chat 请求携带,没有则不传

1.0.5(2026-10-14)

Pinia 实例隔离(修复与宿主同名 store 冲突):

  • 组件库所有 store(user / aiChatSession / small-window)统一通过 useXxxStoreHook() 显式绑定组件库独立的 createPinia() 实例,不再依赖 activePinia——宿主即使存在同名 store(如登录的 defineStore("user"))也互不冲突、互不污染
  • 此前组件内 9 处无参 useUserStore() 会命中宿主 activePinia,若宿主也有 id 为 "user" 的登录 store 会拿到错误结构导致历史列表/收藏失效
  • 删除零引用的 app store

1.0.4(2026-10-14)

token 与用户信息链路重构(含破坏性变更,宿主需同步调整):

  • token 获取统一为 configureAi({ getToken }) 唯一来源:移除 sessionCookie 配置项及 cookie/localStorage 兜底逻辑(utils/auth.ts 整体移除),未注入 getToken 时请求不带 token、不拉取用户信息
  • 用户信息不再 localStorage 落盘缓存:Agent 挂载时按需拉取 /system/user/curUser,刷新/重挂载重新拉取,账号切换天然正确
  • 修复嵌入宿主后不请求 /system/user/curUser 导致历史列表、收藏等接口参数(account)为空的问题(原路由守卫拉取逻辑随路由裁剪丢失)