/** * 组件元数据清单。 * * 这是文档站的唯一数据源,三处消费它: * - `config.ts` 生成侧边栏导航 * - `plugins/docgen.ts` 按 `entry` 定位源码并交给 vue-docgen-api 解析 * - 页面内的 `` 按 `name` 查询解析结果 * * 新增组件时只需在此追加一项,导航、API 表、文档路由会同步生效。 */ /** 分类展示顺序即数组顺序。 */ export const CATEGORY_LIST = [ "布局组件", "编辑器组件", "交互组件", "AI 对话组件" ] as const; export type ComponentCategory = (typeof CATEGORY_LIST)[number]; export interface ComponentMeta { /** 组件名,与组件内 `defineOptions` 的 name 一致,同时是 api-table 的查询键。 */ name: string; /** 文档与侧边栏展示的中文标题。 */ title: string; /** 一句话功能描述。 */ description: string; /** 所属分类。 */ category: ComponentCategory; /** 组件入口源码,相对仓库根目录。 */ entry: string; /** 文档路径覆盖,默认由文件名或目录名推导。 */ slug?: string; } export const COMPONENT_LIST: ComponentMeta[] = [ // ---------- 布局组件 ---------- { name: "PageTransition", title: "PageTransition 页面过渡", description: "为 Vue Router 页面提供可配置的层级滑动过渡与按路由缓存能力。", category: "布局组件", entry: "src/components/page-transition/index.vue" }, { name: "RootLayout", title: "RootLayout 根布局", description: "移动端页面骨架,主体内容之外自动渲染版权与备案信息。", category: "布局组件", entry: "src/components/root-layout/index.vue" }, { name: "Space", title: "Space 间距", description: "控制子元素之间的间距与排列方向,支持分隔符。", category: "布局组件", entry: "src/components/space/index.vue" }, // ---------- 编辑器组件 ---------- { name: "CodeEditor", title: "CodeEditor 代码编辑器", description: "带行号、语法高亮与查找替换的代码输入框。", category: "编辑器组件", entry: "src/components/code-editor/index.vue" }, { name: "TiptapEditor", title: "TiptapEditor 富文本编辑器", description: "基于 Tiptap 的富文本编辑器,工具栏覆盖媒体、表格、链接与代码块。", category: "编辑器组件", entry: "src/components/tiptap-editor/index.vue" }, { name: "TiptapView", title: "TiptapView 富文本视图", description: "Tiptap 内容的只读渲染视图,与编辑器共用同一套样式。", category: "编辑器组件", entry: "src/components/tiptap-view/index.vue" }, { name: "ArticleEditor", title: "ArticleEditor 文章编辑器", description: "以 Article 为数据模型,按正文类型切换纯文本、Markdown 与富文本编辑。", category: "编辑器组件", entry: "src/components/article-editor/index.vue" }, { name: "ArticleView", title: "ArticleView 文章视图", description: "按 Article 的正文类型渲染正文与附件列表。", category: "编辑器组件", entry: "src/components/article-view/index.vue" }, { name: "NotifyEditor", title: "NotifyEditor 通知编辑器", description: "以 NotifyDetail 为数据模型的通知编辑表单,复用文章编辑器能力。", category: "编辑器组件", entry: "src/components/notify-editor/index.vue" }, { name: "NotifyView", title: "NotifyView 通知视图", description: "渲染通知的正文内容。", category: "编辑器组件", entry: "src/components/notify-view/index.vue" }, // ---------- 交互组件 ---------- { name: "BottomPopup", title: "BottomPopup 底部弹层", description: "从底部升起的操作面板,可带标题与左右按钮。", category: "交互组件", entry: "src/components/bottom-popup/index.vue" }, { name: "DurationPicker", title: "DurationPicker 时长选择", description: "按时长值选择并回填,单位与候选项由外部决定。", category: "交互组件", entry: "src/components/duration-picker/index.vue" }, // ---------- AI 对话组件 ---------- { name: "AiChatPanel", title: "AiChatPanel 对话面板", description: "把消息列表与输入框组装成完整对话界面,由业务侧驱动数据。", category: "AI 对话组件", entry: "src/components/ai/AiChatPanel.vue" }, { name: "AiChatMessageList", title: "AiChatMessageList 消息列表", description: "渲染对话消息,含思考过程、引用与流式光标。", category: "AI 对话组件", entry: "src/components/ai/AiChatMessageList.vue" }, { name: "AiChatComposer", title: "AiChatComposer 输入框", description: "对话输入框,按状态在发送、停止与重试之间切换。", category: "AI 对话组件", entry: "src/components/ai/AiChatComposer.vue" }, { name: "AiChatThinking", title: "AiChatThinking 思考过程", description: "折叠展示模型的思考分段,生成中自动展开。", category: "AI 对话组件", entry: "src/components/ai/AiChatThinking.vue" } ]; /** * 文档路径与路由片段。 * * `index.vue` 取所在目录名,具名文件取文件名——`ai` 目录下有多个组件, * 只靠目录名会全部撞成 `ai`。两种写法都会再转成短横线形式。 */ export function toSlug(meta: ComponentMeta): string { if (meta.slug) { return meta.slug; } const segments = meta.entry.split("/"); const fileName = segments[segments.length - 1]; const baseName = fileName.replace(/\.\w+$/, ""); const raw = baseName === "index" ? segments[segments.length - 2] : baseName; return raw.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(); } /** 组件文档页的站内绝对路径。 */ export function toDocLink(meta: ComponentMeta): string { return `/components/${toSlug(meta)}`; } /** 按分类分组,保持 CATEGORY_LIST 的声明顺序。 */ export function groupByCategory(): Array<{ category: ComponentCategory; items: ComponentMeta[] }> { return CATEGORY_LIST.map(category => ({ category, items: COMPONENT_LIST.filter(item => item.category === category) })).filter(group => group.items.length > 0); }