templates/extension-starter/src/web。| 类型 | 导入方式 | 用途 |
|---|---|---|
| 基础 UI 组件 | @buildingai/ui/components/ui/... | 插件页面、控制台页面的按钮、表单、空状态、状态、时间等 |
| AI 展示组件 | @buildingai/ui/components/ai-elements/... | 消息、代码块、工具调用、终端、推理过程等展示 |
| 展示辅助组件 | @buildingai/ui/components/... | 复制、文件图标、图标选择、加载、计数动画等 |
| UI hooks | @buildingai/ui/hooks/... | 确认弹窗、分页、上传、移动端判断等 |
| 页面 hooks | @buildingai/hooks | 复制、页面 head、刷新用户/配置等 |
| 插件请求客户端 | @buildingai/services | 创建插件前台和控制台 HTTP client |
| 共享上传服务 | @buildingai/services/shared | 文件上传、OSS 上传、上传配置缓存 |
| 状态读取 | @buildingai/stores | 读取登录用户、站点配置、用户配置 |
| 插件路由 | @buildingai/web-core | 创建插件前台和控制台路由 |
| 插件 Vite 配置 | @buildingai/web-core/vite/extension | 创建插件前端 Vite 配置 |
import { defineExtensionViteConfig } from "@buildingai/web-core/vite/extension";import { defineExtensionViteConfig } from "@buildingai/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 单独拆为 lucide chunk |
import { defineRouteOption } from "@buildingai/web-core";import { defineRouteOption } from "@buildingai/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 同步父页面 |
import { createPluginHttpClients } from "@buildingai/services";import { createPluginHttpClients } from "@buildingai/services";
const { apiHttpClient, consoleHttpClient } = createPluginHttpClients();
export { apiHttpClient, consoleHttpClient };| 客户端 | 用途 | 对应后端 |
|---|---|---|
apiHttpClient | 插件前台接口 | ExtensionWebController |
consoleHttpClient | 插件控制台接口 | ExtensionConsoleController |
import type {
MutationOptionsUtil,
PaginatedQueryOptionsUtil,
PaginatedResponse,
QueryOptionsUtil,
} from "@buildingai/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,
});
}import { Button } from "@buildingai/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>import { Spinner } from "@buildingai/ui/components/ui/spinner";<Spinner className="size-4" />import {
Empty,
EmptyContent,
EmptyDescription,
EmptyMedia,
EmptyTitle,
} from "@buildingai/ui/components/ui/empty";<Empty>
<EmptyMedia variant="icon">
<Search className="size-5" />
</EmptyMedia>
<EmptyContent>
<EmptyTitle>暂无文章</EmptyTitle>
<EmptyDescription>创建第一篇文章后会显示在这里。</EmptyDescription>
</EmptyContent>
</Empty>import {
Field,
FieldContent,
FieldDescription,
FieldGroup,
FieldLabel,
FieldSet,
} from "@buildingai/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>import { StatusBadge } from "@buildingai/ui/components/ui/status-badge";| prop | 说明 |
|---|---|
active | 是否为启用态 |
activeText | 启用态文案 |
inactiveText | 停用态文案 |
activeVariant | 启用态 badge 样式 |
inactiveVariant | 停用态 badge 样式 |
<StatusBadge active={article.status === "published"} activeText="已发布" inactiveText="草稿" />import { TimeText } from "@buildingai/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" />import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
InputGroupText,
} from "@buildingai/ui/components/ui/input-group";<InputGroup>
<InputGroupAddon>
<Search className="size-4" />
</InputGroupAddon>
<InputGroupInput placeholder="搜索文章" />
<InputGroupButton size="sm">搜索</InputGroupButton>
</InputGroup>import {
Combobox,
ComboboxContent,
ComboboxInput,
ComboboxItem,
ComboboxTrigger,
} from "@buildingai/ui/components/ui/combobox";import { DataTableFacetedFilter } from "@buildingai/ui/components/ui/data-table-faceted-filter";<DataTableFacetedFilter
title="状态"
selectedValue={status}
onSelectionChange={setStatus}
options={[
{ label: "已发布", value: "published" },
{ label: "草稿", value: "draft" },
]}
/>