插件前端组件与工具文档
templates/extension-starter/src/web。导入总览
| 类型 | 导入方式 | 用途 |
|---|---|---|
| 基础 UI 组件 | @chatbuddy-ai/ui/components/ui/... | 插件页面、控制台页面的按钮、表单、空状态、状态、时间等 |
| AI 展示组件 | @chatbuddy-ai/ui/components/ai-elements/... | 消息、代码块、工具调用、终端、推理过程等展示 |
| 展示辅助组件 | @chatbuddy-ai/ui/components/... | 复制、文件图标、图标选择、加载、计数动画等 |
| UI hooks | @chatbuddy-ai/ui/hooks/... | 确认弹窗、分页、上传、移动端判断等 |
| 页面 hooks | @chatbuddy-ai/hooks | 复制、页面 head、刷新用户/配置等 |
| 插件求客户端 | @chatbuddy-ai/services | 创建插件前台和控制台 HTTP client |
| 共享上传服务 | @chatbuddy-ai/services/shared | 文件上传、OSS 上传、上传配置缓存 |
| 状态读取 | @chatbuddy-ai/stores | 读取登录用户、站点配置、用户配置 |
| 插件路由 | @chatbuddy-ai/web-core | 创建插件前台和控制台路由 |
| 插件 Vite 配置 | @chatbuddy-ai/web-core/vite/extension | 创建插件前端 Vite 配置 |
插件 Vite 配置
defineExtensionViteConfig
import { defineExtensionViteConfig } from "@chatbuddy-ai/web-core/vite/extension";import { defineExtensionViteConfig } from "@chatbuddy-ai/web-core/vite/extension"; import packageJson from "./package.json"; export default defineExtensionViteConfig(packageJson, { server: { open: true, },
});| 能力 | 介绍 |
|---|---|
| React | 内置 React Vite 插件 |
| Tailwind | 内置 Tailwind Vite 插件 |
| React Compiler | 内置 React Compiler preset |
| 路径解析 | 启用tsconfigPaths |
| 插件 base | 自动配置为/extension/{packageJson.name} |
| 环境变量目录 | 自动配置envDir: ./../../ |
| 构建输出 | 默认输出.output/public |
| chunk 优化 | lucide-react单独拆为lucidechunk |
插件路由
defineRouteOption
import { defineRouteOption } from "@chatbuddy-ai/web-core";import { defineRouteOption } from "@chatbuddy-ai/web-core"; import packageJson from "./../../package.json";
import ArticleDetailPage from "./pages/article/[id]";
import ArticleListPage from "./pages/console/article/list";
import BlogIndexPage from "./pages/index"; export const routeOption = defineRouteOption({ base: `extension/${packageJson.name}`, identifier: packageJson.name, routes: [ { index: true, element: <BlogIndexPage />, }, { path: "article/:id", element: <ArticleDetailPage />, }, ], consoleMenus: [ { title: "文章管理", path: "/", icon: "file-text", }, ], consoleRoutes: [ { index: true, element: <ArticleListPage />, }, ],
});| 参数 | 类型 | 介绍 |
|---|---|---|
base | string | 插件前端 basename,通常为extension/${packageJson.name} |
identifier | string | 插件标识,用于控制台布局获取插件详情 |
routes | RouteObject[] | 插件前台页面路由 |
consoleMenus | ExtensionMenuItem[] | 插件控制台菜单 |
consoleRoutes | RouteObject[] | 插件控制台页面路由 |
| 能力 | 介绍 |
|---|---|
| 控制台布局 | consoleRoutes自动套ExtensionConsoleLayout |
| 登录守卫 | 控制台路由自动套AuthGuard |
| 全局错误页 | 路由根部接入GlobalError |
| 404 页面 | 未匹配路由渲染插件 404 页面 |
| iframe 路由同步 | 插件路由变化会通过postMessage同步父页面 |
插件求客户端
createPluginHttpClients
import { createPluginHttpClients } from "@chatbuddy-ai/services";import { createPluginHttpClients } from "@chatbuddy-ai/services"; const { apiHttpClient, consoleHttpClient } = createPluginHttpClients; export { apiHttpClient, consoleHttpClient };| 客户端 | 用途 | 对应后端 |
|---|---|---|
apiHttpClient | 插件前台接口 | ExtensionWebController |
consoleHttpClient | 插件控制台接口 | ExtensionConsoleController |
import type { MutationOptionsUtil, PaginatedQueryOptionsUtil, PaginatedResponse, QueryOptionsUtil,
} from "@chatbuddy-ai/web-types";
import { useMutation, useQuery } from "@tanstack/react-query"; import { consoleHttpClient } from "../base";
import type { Article, CreateArticleParams, QueryArticleParams } from "../types/article"; export function useArticleListQuery( params?: QueryArticleParams, options?: PaginatedQueryOptionsUtil<Article>,
) { return useQuery({ queryKey: ["articles", "list", params], queryFn: => consoleHttpClient.get<PaginatedResponse<Article>>("/article", { params }), ...options, });
} export function useArticleDetailQuery(id: string, options?: QueryOptionsUtil<Article>) { return useQuery<Article>({ queryKey: ["articles", "detail", id], queryFn: => consoleHttpClient.get<Article>(`/article/${id}`), enabled: !!id && options?.enabled !== false, ...options, });
} export function useCreateArticleMutation( options?: MutationOptionsUtil<Partial<Article>, CreateArticleParams>,
) { return useMutation<Partial<Article>, Error, CreateArticleParams>({ mutationFn: (data) => consoleHttpClient.post<Partial<Article>>("/article", data), ...options, });
}基础 UI 组件
Button
import { Button } from "@chatbuddy-ai/ui/components/ui/button";| prop | 介绍 |
|---|---|
variant | default、outline、secondary、ghost、destructive、link |
size | default、xs、sm、lg、icon、icon-xs、icon-sm、icon-lg |
loading | 加载态,自动禁用按钮并显示 spinner |
asChild | 将按钮样式传给子元素 |
<Button type="submit" variant="outline" size="sm" loading={isSubmitting}> 提交
</Button>Spinner
import { Spinner } from "@chatbuddy-ai/ui/components/ui/spinner";<Spinner className="size-4" />Empty
import { Empty, EmptyContent, EmptyDescription, EmptyMedia, EmptyTitle,
} from "@chatbuddy-ai/ui/components/ui/empty";<Empty> <EmptyMedia variant="icon"> <Search className="size-5" /> </EmptyMedia> <EmptyContent> <EmptyTitle>暂无文章</EmptyTitle> <EmptyDescription>创建第一篇文章后会显示在这里。</EmptyDescription> </EmptyContent>
</Empty>Field
import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel, FieldSet,
} from "@chatbuddy-ai/ui/components/ui/field";<FieldSet> <FieldGroup> <Field> <FieldLabel>标题</FieldLabel> <FieldContent> <Input value={title} onChange={(event) => setTitle(event.target.value)} /> <FieldDescription>最多 200 个字符。</FieldDescription> </FieldContent> </Field> </FieldGroup>
</FieldSet>StatusBadge
import { StatusBadge } from "@chatbuddy-ai/ui/components/ui/status-badge";| prop | 介绍 |
|---|---|
active | 是否为启用态 |
activeText | 启用态文案 |
inactiveText | 停用态文案 |
activeVariant | 启用态 badge 样式 |
inactiveVariant | 停用态 badge 样式 |
<StatusBadge active={article.status === "published"} activeText="已发布" inactiveText="草稿" />TimeText
import { TimeText } from "@chatbuddy-ai/ui/components/ui/time-text";| prop | 介绍 |
|---|---|
value | Date、字符串或时间戳 |
variant | datetime、date、time、relative |
format | 自定义格式 |
fallback | 空值展示 |
<TimeText value={article.createdAt} variant="datetime" />
<TimeText value={article.updatedAt} variant="relative" />InputGroup
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText,
} from "@chatbuddy-ai/ui/components/ui/input-group";<InputGroup> <InputGroupAddon> <Search className="size-4" /> </InputGroupAddon> <InputGroupInput placeholder="搜索文章" /> <InputGroupButton size="sm">搜索</InputGroupButton>
</InputGroup>Combobox
import { Combobox, ComboboxContent, ComboboxInput, ComboboxItem, ComboboxTrigger,
} from "@chatbuddy-ai/ui/components/ui/combobox";DataTableFacetedFilter
import { DataTableFacetedFilter } from "@chatbuddy-ai/ui/components/ui/data-table-faceted-filter";<DataTableFacetedFilter title="状态" selectedValue={status} onSelectionChange={setStatus} options={[ { label: "已发布", value: "published" }, { label: "草稿", value: "draft" }, ]}
/>上传组件和工具
ImageUpload
import { ImageUpload } from "@chatbuddy-ai/ui/components/ui/image-upload";| prop | 介绍 |
|---|---|
value | 当前图片 URL |
defaultValue | 默认图片 URL |
accept | 文件类型 |
maxSize | 最大大小 |
placeholder | 占位内容 |
onChange | 上传完成后回调 |
onUploadStart | 上传开始回调 |
onUploadError | 上传失败回调 |
size | sm、default、lg、xl |
shape | circle、rounded |
<ImageUpload value={cover} accept="image/*" maxSize={5 * 1024 * 1024} onChange={(url) => setCover(url)}
/>Upload 和 useUpload
import { Upload, useUpload } from "@chatbuddy-ai/ui/components/upload";| 能力 | 介绍 |
|---|---|
multiple | 多文件上传 |
accept | 限制文件类型 |
maxSize | 限制单文件大小 |
maxFiles | 限制文件数量 |
params | 透 传上传参数 |
onUploadProgress | 上传进度 |
onUploadSuccess | 单文件成功 |
onUploadError | 上传失败 |
onUploadComplete | 批量完成 |
useUploadFile
import { useUploadFile } from "@chatbuddy-ai/ui/hooks/use-upload-file";| 字段 | 介绍 |
|---|---|
isUploading | 是否上传中 |
progress | 上传进度 |
uploadedFile | 上传完成文件 |
uploadFile | 执行上传 |
uploadingFile | 当前上传文件 |
共享上传服务
import { detectFileType, invalidateStorageConfigCache, uploadFile, uploadFileAuto, uploadFiles, uploadFilesAuto, uploadInitFile,
} from "@chatbuddy-ai/services/shared";| 工具 | 介绍 |
|---|---|
uploadFile | 上传单文件 |
uploadFiles | 上传多文件 |
uploadInitFile | 系统初始化阶段上传文件 |
uploadFileAuto | 自动选择上传方式上传单文件 |
uploadFilesAuto | 自动选择上传方式上传多文件 |
detectFileType | 识别文件类型 |
invalidateStorageConfigCache | 清理上传配置缓存 |
AI 展示组件
Message
import { Message, MessageContent } from "@chatbuddy-ai/ui/components/ai-elements/message";<Message from="assistant"> <MessageContent>生成完成。</MessageContent>
</Message>CodeBlock
import { CodeBlock, CodeBlockCopyButton, CodeBlockHeader, CodeBlockTitle,
} from "@chatbuddy-ai/ui/components/ai-elements/code-block";<CodeBlock code={source} language="tsx" showLineNumbers> <CodeBlockHeader> <CodeBlockTitle>example.tsx</CodeBlockTitle> <CodeBlockCopyButton /> </CodeBlockHeader>
</CodeBlock>Tool
import { Tool, ToolContent, ToolHeader, ToolOutput,
} from "@chatbuddy-ai/ui/components/ai-elements/tool";<Tool> <ToolHeader type="tool-call" state="output-available" /> <ToolContent> <ToolOutput output="执行完成" /> </ToolContent>
</Tool>其他 AI 展示组件
| 组件路径 | 可用组件 |
|---|---|
ai-elements/conversation | 对话容器 |
ai-elements/prompt-input | 提示词输入框 |
ai-elements/reasoning | 推理过程 |
ai-elements/terminal | 终端输出 |
ai-elements/sources | 来源引用 |
ai-elements/file-tree | 文件树 |
ai-elements/task | 任务状态 |
ai-elements/tests | 测试结果 |
ai-elements/stack-trace | 错误堆栈 |
ai-elements/preview | 预览内容 |
展示辅助组件
CopyButton
import { CopyButton } from "@chatbuddy-ai/ui/components/copy-button";<CopyButton value={article.url} size="icon-sm" />FileFormatIcon
import { FileFormatIcon } from "@chatbuddy-ai/ui/components/file-format-icon";<FileFormatIcon filename={file.name} />LucideIcon
import { LucideIcon } from "@chatbuddy-ai/ui/components/lucide-icon";<LucideIcon name="file-text" className="size-4" />IconPicker
import { IconPicker } from "@chatbuddy-ai/ui/components/icon-picker";| prop | 介绍 |
|---|---|
value | 当前图标名 |
onChange | 选择变化回调 |
placeholder | 占位文案 |
disabled | 禁用 |
container | Popover 容器 |
<IconPicker value={icon} onChange={setIcon} placeholder="选择菜单图标" />Loader、ReloadWindow、CountUp、SplitText
import Loader from "@chatbuddy-ai/ui/components/loader";
import ReloadWindow from "@chatbuddy-ai/ui/components/reload-window";
import { CountUp } from "@chatbuddy-ai/ui/components/count-up";
import { SplitText } from "@chatbuddy-ai/ui/components/split-text";| 组件 | 场景 |
|---|---|
Loader | 页面或局部加载 |
ReloadWindow | 提示刷新页面 |
CountUp | 数字动画 |
SplitText | 文本拆分动画 |
常用 hooks
useAlertDialog
import { useAlertDialog } from "@chatbuddy-ai/ui/hooks/use-alert-dialog";const { confirm } = useAlertDialog; await confirm({ title: "移除文章", description: "移除后不可恢复,确认继续?",
});usePagination
import { usePagination } from "@chatbuddy-ai/ui/hooks/use-pagination";| 字段 | 介绍 |
|---|---|
currentPage | 当前页 |
totalPages | 总页数 |
pageSize | 每页数量 |
total | 总条数 |
hasPrevious | 是否有上一页 |
hasNext | 是否有下一页 |
goToPage | 跳转页码 |
nextPage | 下一页 |
previousPage | 上一页 |
reset | 重置分页 |
PaginationComponent | 分页组件 |
useCopy
import { useCopy } from "@chatbuddy-ai/hooks";const { copy, isCopying } = useCopy; await copy(article.url);useDocumentHead
import { useDocumentHead } from "@chatbuddy-ai/hooks";useDocumentHead({ title: "文章管理",
});其他可用 hooks
| hook | 导入 | 场景 |
|---|---|---|
useMobile | @chatbuddy-ai/ui/hooks/use-mobile | 判断移动端 |
useMounted | @chatbuddy-ai/ui/hooks/use-mounted | 判断组件是否已挂载 |
useDebounce | @chatbuddy-ai/ui/hooks/use-debounce | 输入防抖 |
useRefreshUser | @chatbuddy-ai/hooks | 刷新当前用户 |
useRefreshUserConfig | @chatbuddy-ai/hooks | 刷新用户配置 |
useRefreshWebsiteConfig | @chatbuddy-ai/hooks | 刷新站点配置 |
状态读取
import { useAuthStore, useConfigStore, useUserConfigStore } from "@chatbuddy-ai/stores";| store | 介绍 |
|---|---|
useAuthStore | 当前登录状态、用户信息、权限码 |
useConfigStore | 站点配置 |
useUserConfigStore | 用户配置 |
const userInfo = useAuthStore((state) => state.auth.userInfo);
const websiteConfig = useConfigStore((state) => state.config);类型工具
import type { MutationOptionsUtil, PaginatedQueryOptionsUtil, PaginatedResponse, QueryOptionsUtil,
} from "@chatbuddy-ai/web-types";| 类型 | 介绍 |
|---|---|
PaginatedResponse<T> | 分页响应 |
QueryOptionsUtil<T> | React Query 查询 options |
PaginatedQueryOptionsUtil<T> | 分页查询 options |
MutationOptionsUtil<TData, TVariables> | mutation options |
共 319 篇文档 · 内容同步自官方帮助中心