mirror of
https://github.com/halo-dev/docs.git
synced 2025-10-21 10:17:34 +00:00
docs: update documentation for Halo 2.18 (#407)
Signed-off-by: Ryan Wang <i@ryanc.cc>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
---
|
||||
title: API 请求
|
||||
description: 介绍如何在插件的 UI 中请求 API 接口
|
||||
---
|
||||
|
||||
在 2.17.0 版本中,Halo 提供了新的 `@halo-dev/api-client` 包,用于简化在 Halo 内部、插件的 UI 中、外部应用程序中请求 Halo 接口的逻辑。此文档将介绍如何在插件的 UI 中使用 `@halo-dev/api-client` 包。
|
||||
|
||||
## 安装
|
||||
|
||||
```shell
|
||||
pnpm install @halo-dev/api-client axios
|
||||
```
|
||||
|
||||
## 模块介绍
|
||||
|
||||
在 `@halo-dev/api-client` 包中导出了以下模块:
|
||||
|
||||
```ts
|
||||
import {
|
||||
coreApiClient,
|
||||
consoleApiClient,
|
||||
ucApiClient,
|
||||
publicApiClient,
|
||||
createCoreApiClient,
|
||||
createConsoleApiClient,
|
||||
createUcApiClient,
|
||||
createPublicApiClient,
|
||||
axiosInstance
|
||||
} from "@halo-dev/api-client"
|
||||
```
|
||||
|
||||
- **coreApiClient**: 为 Halo 所有自定义模型的 CRUD 接口封装的 api client。
|
||||
- **consoleApiClient**: 为 Halo 针对 Console 提供的接口封装的 api client。
|
||||
- **ucApiClient**: 为 Halo 针对 UC 提供的接口封装的 api client。
|
||||
- **publicApiClient**: 为 Halo 所有公开访问的接口封装的 api client。
|
||||
- **createCoreApiClient**: 用于创建自定义模型的 CRUD 接口封装的 api client,需要传入 axios 实例。
|
||||
- **createConsoleApiClient**: 用于创建 Console 接口封装的 api client,需要传入 axios 实例。
|
||||
- **createUcApiClient**: 用于创建 UC 接口封装的 api client,需要传入 axios 实例。
|
||||
- **createPublicApiClient**: 用于创建公开访问接口封装的 api client,需要传入 axios 实例。
|
||||
- **axiosInstance**: 内部默认创建的 axios 实例。
|
||||
|
||||
## 使用
|
||||
|
||||
在 Halo 的插件项目中,如果是调用 Halo 内部的接口,那么直接使用上面介绍的模块即可,无需任何配置,在 Halo 内部已经处理好了异常逻辑,包括登录失效、无权限等。
|
||||
|
||||
其中,`coreApiClient`、`consoleApiClient`、`ucApiClient`、`publicApiClient` 模块是对 Halo 内部所有 API 请求的封装,无需传入任何请求地址,比如:
|
||||
|
||||
```ts
|
||||
import { coreApiClient } from "@halo-dev/api-client"
|
||||
|
||||
coreApiClient.content.post.listPost().then(response => {
|
||||
// handle response
|
||||
})
|
||||
```
|
||||
|
||||
如果需要调用插件提供的接口,可以直接使用 `axiosInstance` 实例,比如:
|
||||
|
||||
```ts
|
||||
import { axiosInstance } from "@halo-dev/api-client"
|
||||
|
||||
axiosInstance.get("/apis/foo.halo.run/v1alpha1/bar").then(response => {
|
||||
// handle response
|
||||
})
|
||||
```
|
||||
|
||||
此外,在最新的 `@halo-dev/ui-plugin-bundler-kit@2.17.0` 中,已经排除了 `@halo-dev/api-client`、`axios` 依赖,所以最终产物中的相关依赖会自动使用 Halo 本身提供的依赖,无需关心最终产物大小。
|
||||
|
||||
:::info 提醒
|
||||
如果插件中使用了 `@halo-dev/api-client@2.17.0` 和 `@halo-dev/ui-plugin-bundler-kit@2.17.0`,需要提升 `plugin.yaml` 中的 `spec.requires` 版本为 `>=2.17.0`。
|
||||
:::
|
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: AnnotationsForm
|
||||
description: 元数据表单组件
|
||||
---
|
||||
|
||||
此组件用于提供统一的 [Annotations 表单](../../../../annotations-form.md),可以根据 `group` 和 `kind` 属性自动渲染对应的表单项。
|
||||
|
||||
## 使用示例
|
||||
|
||||
```html
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue"
|
||||
|
||||
const annotationsFormRef = ref()
|
||||
const currentAnnotations = ref()
|
||||
|
||||
function handleSubmit () {
|
||||
annotationsFormRef.value?.handleSubmit();
|
||||
await nextTick();
|
||||
|
||||
const { customAnnotations, annotations, customFormInvalid, specFormInvalid } =
|
||||
annotationsFormRef.value || {};
|
||||
|
||||
// 表单验证不通过
|
||||
if (customFormInvalid || specFormInvalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 合并自定义数据和表单提供的数据
|
||||
const newAnnotations = {
|
||||
...annotations,
|
||||
...customAnnotations,
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AnnotationsForm
|
||||
ref="annotationsFormRef"
|
||||
:value="currentAnnotations"
|
||||
kind="Post"
|
||||
group="content.halo.run"
|
||||
/>
|
||||
|
||||
<VButton @click="handleSubmit">提交</VButton>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
|---------|------------------------------------|---------|-----------------------------------------|
|
||||
| `group` | string | 无,必填 | 定义组件所属的分组。 |
|
||||
| `kind` | string | 无,必填 | 定义组件的种类。 |
|
||||
| `value` | \{ [key: string]: string; \} \| null \| null | 可选,包含键值对的对象或空值,用于存储数据。 |
|
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: AttachmentFileTypeIcon
|
||||
description: 附件文件类型图标组件
|
||||
---
|
||||
|
||||
此组件用于根据文件名显示文件类型图标。
|
||||
|
||||
## 使用示例
|
||||
|
||||
```html
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<AttachmentFileTypeIcon fileName="example.png" />
|
||||
</div>
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
|--------------|---------------------|-----------|------------------------------------|
|
||||
| `fileName` | string \| undefined | undefined | 文件名,可以是字符串或未定义。 |
|
||||
| `displayExt` | boolean | true | 可选,是否显示文件扩展名,默认为 true。 |
|
||||
| `width` | number | 10 | 可选,组件宽度,默认为 10。 |
|
||||
| `height` | number | 10 | 可选,组件高度,默认为 10。 |
|
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: AttachmentSelectorModal
|
||||
description: 附件选择组件
|
||||
---
|
||||
|
||||
此组件用于调出附件选择器,以供用户选择附件。
|
||||
|
||||
:::info 注意
|
||||
此组件当前仅在 Console 中可用。
|
||||
:::
|
||||
|
||||
## 使用示例
|
||||
|
||||
```html
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue"
|
||||
|
||||
const visible = ref(false)
|
||||
|
||||
function onAttachmentSelect (attachments: AttachmentLike[]) {
|
||||
console.log(attachments)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VButton @click="visible = true">选择附件</VButton>
|
||||
|
||||
<AttachmentSelectorModal
|
||||
v-model:visible="visible"
|
||||
@select="onAttachmentSelect"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
|-----------|----------|---------------|--------------------------|
|
||||
| `visible` | boolean | false | 控制组件是否可见。 |
|
||||
| `accepts` | string[] | () => ["*/*"] | 可选,定义可接受的文件类型。 |
|
||||
| `min` | number | undefined | 可选,定义最小选择数量。 |
|
||||
| `max` | number | undefined | 可选,定义最大选择数量。 |
|
||||
|
||||
## Emits
|
||||
|
||||
| 事件名称 | 参数 | 描述 |
|
||||
|----------------|---------------------------------------------------|---------------------|
|
||||
| update:visible | `visible`: boolean 类型,表示可见状态。 | 当可见状态更新时触发。 |
|
||||
| close | 无 | 当弹框关闭时触发。 |
|
||||
| select | `attachments`: AttachmentLike[] 类型,表示附件数组。 | 当选择确定按钮时触发。 |
|
@@ -0,0 +1,18 @@
|
||||
---
|
||||
title: FilterCleanButton
|
||||
description: 过滤器清除按钮组件
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
```html
|
||||
<script lang="ts" setup>
|
||||
function onClear () {
|
||||
console.log("clear")
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FilterCleanButton @click="onClear" />
|
||||
</template>
|
||||
```
|
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: FilterDropdown
|
||||
description: 过滤器下拉组件
|
||||
---
|
||||
|
||||
此组件为通用的下拉筛选组件,可以接收一个对象数组作为选项,并使用 `v-model` 绑定选择的值。
|
||||
|
||||
## 使用示例
|
||||
|
||||
```html
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue"
|
||||
|
||||
const value = ref("")
|
||||
const items = [
|
||||
{
|
||||
label: "最近创建",
|
||||
value: "creationTimestamp,desc"
|
||||
},
|
||||
{
|
||||
label: "较晚创建",
|
||||
value: "creationTimestamp,asc"
|
||||
}
|
||||
]
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<FilterDropdown
|
||||
v-model="value"
|
||||
label="排序"
|
||||
:items="items"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
|--------------|-----------------------------------------------------------|-----------|--------------------------------------------------|
|
||||
| `items` | \{ label: string; value?: string \| boolean \| number; \}[] | 无,必填 | 包含 `label` 和可选 `value` 的对象数组。 |
|
||||
| `label` | string | 无,必填 | 组件的标签文本。 |
|
||||
| `modelValue` | string \| boolean \| number | undefined | 可选,用于绑定到组件的值,可以是字符串、布尔值或数字。 |
|
||||
|
||||
## Emits
|
||||
|
||||
| 事件名称 | 参数 | 描述 |
|
||||
|-------------------|--------------------------------------------------------|-------------------|
|
||||
| update:modelValue | `modelValue`: string \| boolean \| number \| undefined | 当模型值更新时触发。 |
|
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: HasPermission
|
||||
description: 权限判断组件
|
||||
---
|
||||
|
||||
此组件用于根据权限控制元素的显示与隐藏。
|
||||
|
||||
## 使用方式
|
||||
|
||||
```html
|
||||
<script lang="ts" setup>
|
||||
import { VButton } from "@halo-dev/components"
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<HasPermission :permissions="['system:posts:manage']">
|
||||
<VButton type="danger">删除</VButton>
|
||||
</HasPermission>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
|---------------|----------|------|-----------------------|
|
||||
| `permissions` | string[] | 无,必填 | 定义组件所需的权限列表。 |
|
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: 组件
|
||||
description: 在 Halo UI 中可使用的组件。
|
||||
---
|
||||
|
||||
此文档将介绍所有在插件中可用的组件,以及它们的使用方法和区别。
|
||||
|
||||
## 基础组件库
|
||||
|
||||
我们为 Halo 的前端封装了一个基础组件的库,你可以在插件中使用这些组件。
|
||||
|
||||
安装方式:
|
||||
|
||||
```bash
|
||||
pnpm install @halo-dev/components
|
||||
```
|
||||
|
||||
在 Vue 组件中:
|
||||
|
||||
```html
|
||||
<script lang="ts" setup>
|
||||
import { VButton } from "@halo-dev/components";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VButton>Hello</VButton>
|
||||
</template>
|
||||
```
|
||||
|
||||
所有可用的基础组件以及文档可查阅:[https://halo-ui-components.pages.dev](https://halo-ui-components.pages.dev)
|
||||
|
||||
## 业务组件和指令
|
||||
|
||||
除了基础组件库,我们还为 Halo 的前端封装了一些业务组件和指令,这些组件已经在全局注册,你可以直接在插件中使用这些组件和指令。
|
||||
|
||||
以下是所有可用的业务组件和指令:
|
||||
|
||||
```mdx-code-block
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
|
||||
<DocCardList />
|
||||
```
|
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: PluginDetailModal
|
||||
description: 插件详情弹窗组件
|
||||
---
|
||||
|
||||
此组件可以在 UI 部分的任意组件中打开差价的详情和设置弹窗,可以用于实现在不打断正常操作流程的情况下让用户查看和修改插件的详细信息。
|
||||
|
||||
## 使用方式
|
||||
|
||||
```html
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue"
|
||||
|
||||
const modalVisible = ref(false)
|
||||
|
||||
function onPluginDetailModalClose() {
|
||||
// Do something
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PluginDetailModal v-if="modalVisible" @close="onPluginDetailModalClose" name="starter" />
|
||||
</template>
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| ------ | ------ | -------- | --------------------------------------------- |
|
||||
| `name` | string | 无,必填 | 插件名称,即 plugin.yaml 中的 `metadata.name` |
|
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: SearchInput
|
||||
description: 搜索输入框组件
|
||||
---
|
||||
|
||||
此组件适用于关键词搜索场景,输入数据的过程中不会触发搜索,只有在输入完成后,点击回车才会触发搜索。
|
||||
|
||||
## 使用方式
|
||||
|
||||
```html
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue"
|
||||
|
||||
const keyword = ref("")
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SearchInput v-model="keyword" placeholder="请输入关键字" />
|
||||
</template>
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
|---------------|--------|-----------|----------------------------------|
|
||||
| `placeholder` | string | undefined | 可选,用于指定输入字段的占位符文本。 |
|
||||
| `modelValue` | string | 无,必填 | 用于绑定输入字段的值。 |
|
||||
|
||||
## Emits
|
||||
|
||||
| 事件名称 | 参数 | 描述 |
|
||||
|-------------------|-------------------------------------|-------------------|
|
||||
| update:modelValue | `modelValue`: string 类型,表示模型值。 | 当模型值更新时触发。 |
|
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: UppyUpload
|
||||
description: 文件上传组件
|
||||
---
|
||||
|
||||
## 使用方式
|
||||
|
||||
```html
|
||||
<script lang="ts" setup>
|
||||
const policyName = ref('my-test-policy')
|
||||
const groupName = ref('my-test-group')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UppyUpload
|
||||
endpoint="/apis/api.console.halo.run/v1alpha1/attachments/upload"
|
||||
:meta="{
|
||||
policyName: policyName,
|
||||
groupName: groupName,
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
|---------------------|----------------------------------------------------------------|-----------|------------------------------|
|
||||
| `restrictions` | Restrictions | undefined | 可选,指定任何限制。 |
|
||||
| `meta` | Record\<string, unknown\> | undefined | 可选,要发送的额外元数据。 |
|
||||
| `autoProceed` | boolean | false | 可选,在某些操作后自动继续。 |
|
||||
| `allowedMetaFields` | string[] | undefined | 可选,指定允许的元数据字段。 |
|
||||
| `endpoint` | string | 无,必填 | 数据发送到的端点URL。 |
|
||||
| `name` | string | file | 可选,用于上传的表单字段的名称。 |
|
||||
| `note` | string | undefined | 可选,任何备注或描述。 |
|
||||
| `method` | "GET" \| "POST" \| "PUT" \| "HEAD" \| "get" \| "post" \| "put" | post | 可选,用于请求的HTTP方法。 |
|
||||
| `disabled` | boolean | false | 可选,如果为真,则禁用组件。 |
|
||||
| `width` | string | 750px | 可选,组件的宽度。 |
|
||||
| `height` | string | 550px | 可选,组件的高度。 |
|
||||
| `doneButtonHandler` | () => void | undefined | 可选,完成时调用的处理函数。 |
|
||||
|
||||
## Emits
|
||||
|
||||
| 事件名称 | 参数 | 描述 |
|
||||
|----------|------------------------------------------------------|---------------------|
|
||||
| uploaded | `response`: SuccessResponse 类型,表示上传成功的响应。 | 当文件上传成功时触发。 |
|
||||
| error | `file`: 出错的文件。<br />`response`: 出错时的响应数据。 | 当文件上传出错时触发。 |
|
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: VCodemirror
|
||||
description: 代码编辑器组件
|
||||
---
|
||||
|
||||
此组件封装了 Codemirror 代码编辑器,适用于一些需要编辑代码的场景。
|
||||
|
||||
## 使用方式
|
||||
|
||||
```html
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue"
|
||||
|
||||
const value = ref("")
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VCodemirror v-model="value" height="300px" language="html" />
|
||||
</template>
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
|--------------|-------------------------------------------------|----------|-------------------------------------------|
|
||||
| `modelValue` | string | "" | 可选,绑定到组件的字符串值,默认为空字符串。 |
|
||||
| `height` | string | auto | 可选,组件的高度,默认为 `"auto"`。 |
|
||||
| `language` | keyof typeof presetLanguages \| LanguageSupport | yaml | 代码编辑器的语言支持,默认为 `"yaml"`。 |
|
||||
| `extensions` | EditorStateConfig["extensions"] | () => [] | 可选,编辑器状态配置的扩展,默认为一个空数组。 |
|
||||
|
||||
## Emits
|
||||
|
||||
| 事件名称 | 参数 | 描述 |
|
||||
|-------------------|----------------------------------|-------------------|
|
||||
| update:modelValue | `value`: string 类型,表示模型值。 | 当模型值更新时触发。 |
|
||||
| change | `value`: string 类型,表示变更的值。 | 当值发生变化时触发。 |
|
@@ -0,0 +1,18 @@
|
||||
---
|
||||
title: v-permission
|
||||
description: 权限指令
|
||||
---
|
||||
|
||||
与 [HasPermission](./has-permission.md) 组件相同,此指令也是用于根据权限控制元素的显示与隐藏。
|
||||
|
||||
## 使用方式
|
||||
|
||||
```html
|
||||
<script lang="ts" setup>
|
||||
import { VButton } from "@halo-dev/components"
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VButton type="danger" v-permission="['system:posts:manage']">删除</VButton>
|
||||
</template>
|
||||
```
|
@@ -0,0 +1,18 @@
|
||||
---
|
||||
title: v-tooltip
|
||||
description: Tooltip 指令
|
||||
---
|
||||
|
||||
此指令用于在任何元素上添加一个提示框。
|
||||
|
||||
## 使用方式
|
||||
|
||||
```html
|
||||
<script lang="ts" setup>
|
||||
import { IconDeleteBin } from "@halo-dev/components"
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<IconDeleteBin v-tooltip="'删除此文档'" />
|
||||
</template>
|
||||
```
|
@@ -0,0 +1,98 @@
|
||||
---
|
||||
title: 附件数据列表操作菜单
|
||||
description: 扩展附件数据列表操作菜单 - attachment:list-item:operation:create
|
||||
---
|
||||
|
||||
此扩展点用于扩展附件数据列表的操作菜单项。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"attachment:list-item:operation:create": (
|
||||
attachment: Ref<Attachment>
|
||||
): OperationItem<Attachment>[] | Promise<OperationItem<Attachment>[]> => {
|
||||
return [
|
||||
{
|
||||
priority: 10,
|
||||
component: markRaw(VDropdownItem),
|
||||
props: {},
|
||||
action: (item?: Attachment) => {
|
||||
// do something
|
||||
},
|
||||
label: "foo",
|
||||
hidden: false,
|
||||
permissions: [],
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```mdx-code-block
|
||||
import OperationItem from "./interface/OperationItem.md";
|
||||
|
||||
<OperationItem />
|
||||
```
|
||||
|
||||
## 示例
|
||||
|
||||
此示例将实现一个下载附件到本地的操作菜单项。
|
||||
|
||||
```ts
|
||||
import { definePlugin, type OperationItem } from "@halo-dev/console-shared";
|
||||
import { Toast, VDropdownItem } from "@halo-dev/components";
|
||||
import { markRaw, type Ref } from "vue";
|
||||
import type { Attachment } from "@halo-dev/api-client";
|
||||
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"attachment:list-item:operation:create": (
|
||||
attachment: Ref<Attachment>
|
||||
): OperationItem<Attachment>[] | Promise<OperationItem<Attachment>[]> => {
|
||||
return [
|
||||
{
|
||||
priority: 10,
|
||||
component: markRaw(VDropdownItem),
|
||||
props: {},
|
||||
action: (item?: Attachment) => {
|
||||
if (!item?.status?.permalink) {
|
||||
Toast.error("该附件没有下载地址");
|
||||
return;
|
||||
}
|
||||
|
||||
const a = document.createElement("a");
|
||||
a.href = item.status.permalink;
|
||||
a.download = item?.spec.displayName || item.metadata.name;
|
||||
a.click();
|
||||
},
|
||||
label: "下载",
|
||||
hidden: false,
|
||||
permissions: [],
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
## 实现案例
|
||||
|
||||
- [https://github.com/halo-dev/plugin-s3](https://github.com/halo-dev/plugin-s3)
|
||||
|
||||
## 类型定义
|
||||
|
||||
### Attachment
|
||||
|
||||
```mdx-code-block
|
||||
import Attachment from "./interface/Attachment.md";
|
||||
|
||||
<Attachment />
|
||||
```
|
@@ -0,0 +1,146 @@
|
||||
---
|
||||
title: 附件选择选项卡
|
||||
description: 扩展附件选择组件的选项卡 - attachment:selector:create
|
||||
---
|
||||
|
||||
此扩展点用于扩展附件选择组件的选项卡,目前 Halo 仅包含内置的附件库,你可以通过此扩展点添加自定义的选项卡。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"attachment:selector:create": (): AttachmentSelectProvider[]| Promise<AttachmentSelectProvider[]> => {
|
||||
return [
|
||||
{
|
||||
id: "foo",
|
||||
label: "foo",
|
||||
component: markRaw(FooComponent),
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts title="AttachmentSelectProvider"
|
||||
export interface AttachmentSelectProvider {
|
||||
id: string; // 选项卡 ID
|
||||
label: string; // 选项卡名称
|
||||
component: Component | string; // 选项卡组件
|
||||
}
|
||||
```
|
||||
|
||||
其中,`component` 可以是组件对象或组件名称,且此组件有以下实现要求:
|
||||
|
||||
1. 组件必须包含名称为 `selected` 的 `prop`,用于接收当前选中的附件。
|
||||
|
||||
```ts
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
selected: AttachmentLike[];
|
||||
}>(),
|
||||
{
|
||||
selected: () => [],
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
2. 组件必须包含名称为 `update:selected` 的 emit 事件,用于更新选中的附件。
|
||||
|
||||
```ts
|
||||
const emit = defineEmits<{
|
||||
(event: "update:selected", attachments: AttachmentLike[]): void;
|
||||
}>();
|
||||
```
|
||||
|
||||
```ts title="AttachmentLike"
|
||||
export type AttachmentLike =
|
||||
| Attachment
|
||||
| string
|
||||
| {
|
||||
url: string;
|
||||
type: string;
|
||||
};
|
||||
```
|
||||
|
||||
## 示例
|
||||
|
||||
为附件选择组件添加 Stickers 选项卡,用于从给定的贴纸列表选择附件。
|
||||
|
||||
```ts title="index.ts"
|
||||
import {
|
||||
definePlugin,
|
||||
type AttachmentSelectProvider,
|
||||
} from "@halo-dev/console-shared";
|
||||
import { markRaw } from "vue";
|
||||
import StickerSelectorProvider from "./components/StickerSelectorProvider.vue";
|
||||
|
||||
export default definePlugin({
|
||||
components: {},
|
||||
routes: [],
|
||||
extensionPoints: {
|
||||
"attachment:selector:create": (): AttachmentSelectProvider[] => {
|
||||
return [
|
||||
{
|
||||
id: "stickers",
|
||||
label: "Stickers",
|
||||
component: markRaw(StickerSelectorProvider),
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```html title="StickerSelectorProvider.vue"
|
||||
<script lang="ts" setup>
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
selected: AttachmentLike[];
|
||||
}>(),
|
||||
{
|
||||
selected: () => [],
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: "update:selected", attachments: AttachmentLike[]): void;
|
||||
}>();
|
||||
|
||||
const stickers = [
|
||||
{
|
||||
url: "https://picsum.photos/200?random=1",
|
||||
},
|
||||
{
|
||||
url: "https://picsum.photos/200?random=2",
|
||||
},
|
||||
{
|
||||
url: "https://picsum.photos/200?random=3",
|
||||
},
|
||||
];
|
||||
|
||||
const selectedStickers = ref<Set<String>>(new Set());
|
||||
|
||||
const handleSelect = async (url: string) => {
|
||||
if (selectedStickers.value.has(url)) {
|
||||
selectedStickers.value.delete(url);
|
||||
return;
|
||||
}
|
||||
selectedStickers.value.add(url);
|
||||
emit('update:selected', Array.from(selectedStickers.value));
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<img v-for="sticker in stickers" :src="sticker.url" @click="handleSelect(sticker.url)" />
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 实现案例
|
||||
|
||||
- [https://github.com/halo-sigs/plugin-unsplash](https://github.com/halo-sigs/plugin-unsplash)
|
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: 备份数据列表操作菜单
|
||||
description: 扩展备份数据列表操作菜单 - backup:list-item:operation:create
|
||||
---
|
||||
|
||||
此扩展点用于扩展备份数据列表的操作菜单项。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"backup:list-item:operation:create": (
|
||||
backup: Ref<Backup>
|
||||
): OperationItem<Backup>[] | Promise<OperationItem<Backup>[]> => {
|
||||
return [
|
||||
{
|
||||
priority: 10,
|
||||
component: markRaw(VDropdownItem),
|
||||
props: {},
|
||||
action: (item?: Backup) => {
|
||||
// do something
|
||||
},
|
||||
label: "foo",
|
||||
hidden: false,
|
||||
permissions: [],
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```mdx-code-block
|
||||
import OperationItem from "./interface/OperationItem.md";
|
||||
|
||||
<OperationItem />
|
||||
```
|
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: 备份页面选项卡
|
||||
description: 扩展备份页面选项卡 - backup:tabs:create
|
||||
---
|
||||
|
||||
此扩展点可以针对备份页面扩展更多关于 UI 的功能,比如定时备份设置、备份到第三方云存储等。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"backup:tabs:create": (): BackupTab[] | Promise<BackupTab[]> => {
|
||||
return [
|
||||
{
|
||||
id: "foo",
|
||||
label: "foo",
|
||||
component: markRaw(FooComponent),
|
||||
permissions: [],
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts title="BackupTab"
|
||||
export interface BackupTab {
|
||||
id: string; // 选项卡 ID
|
||||
label: string; // 选项卡标题
|
||||
component: Raw<Component>; // 选项卡面板组件
|
||||
permissions?: string[]; // 选项卡权限
|
||||
}
|
||||
```
|
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: 评论数据列表操作菜单
|
||||
description: 扩展评论数据列表操作菜单 - comment:list-item:operation:create
|
||||
---
|
||||
|
||||
此扩展点用于扩展评论数据列表的操作菜单项。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"comment:list-item:operation:create": (
|
||||
comment: Ref<ListedComment>
|
||||
): OperationItem<ListedComment>[] | Promise<OperationItem<ListedComment>[]> => {
|
||||
return [
|
||||
{
|
||||
priority: 10,
|
||||
component: markRaw(VDropdownItem),
|
||||
props: {},
|
||||
action: (item?: ListedComment) => {
|
||||
// do something
|
||||
},
|
||||
label: "foo",
|
||||
hidden: false,
|
||||
permissions: [],
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```mdx-code-block
|
||||
import OperationItem from "./interface/OperationItem.md";
|
||||
|
||||
<OperationItem />
|
||||
```
|
||||
|
||||
## 示例
|
||||
|
||||
此示例将实现一个操作菜单项。
|
||||
|
||||
```ts
|
||||
import type { ListedComment } from "@halo-dev/api-client";
|
||||
import { VDropdownItem } from "@halo-dev/components";
|
||||
import { definePlugin } from "@halo-dev/console-shared";
|
||||
import { markRaw } from "vue";
|
||||
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"comment:list-item:operation:create": () => {
|
||||
return [
|
||||
{
|
||||
priority: 21,
|
||||
component: markRaw(VDropdownItem),
|
||||
label: "测试评论菜单",
|
||||
visible: true,
|
||||
permissions: [],
|
||||
action: async (comment: ListedComment) => {
|
||||
console.log(comment)
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### ListedComment
|
||||
|
||||
```mdx-code-block
|
||||
import ListedComment from "./interface/ListedComment.md";
|
||||
|
||||
<ListedComment />
|
||||
```
|
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: 评论来源显示
|
||||
description: 扩展评论来源显示 - comment:subject-ref:create
|
||||
---
|
||||
|
||||
Console 的评论管理列表的评论来源默认仅支持显示来自文章和页面的评论,如果其他插件中的业务模块也使用了评论,那么就可以通过该拓展点来扩展评论来源的显示。
|
||||
|
||||
:::info 提示
|
||||
此扩展点需要后端配合使用,请参考 [评论主体展示](../../server/extension-points/comment-subject.md)。
|
||||
:::
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"comment:subject-ref:create": (): CommentSubjectRefProvider[] => {
|
||||
return [
|
||||
{
|
||||
kind: "Example",
|
||||
group: "example.halo.run",
|
||||
resolve: (subject: Extension): CommentSubjectRefResult => {
|
||||
return {
|
||||
label: "foo",
|
||||
title: subject.title,
|
||||
externalUrl: `/example/${subject.metadata.name}`,
|
||||
route: {
|
||||
name: "Example",
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts title="CommentSubjectRefProvider"
|
||||
export type CommentSubjectRefProvider = {
|
||||
kind: string;
|
||||
group: string;
|
||||
resolve: (subject: Extension) => CommentSubjectRefResult;
|
||||
};
|
||||
```
|
||||
|
||||
```ts title="CommentSubjectRefResult"
|
||||
export interface CommentSubjectRefResult {
|
||||
label: string;
|
||||
title: string;
|
||||
route?: RouteLocationRaw;
|
||||
externalUrl?: string;
|
||||
}
|
||||
```
|
||||
|
||||
## 示例
|
||||
|
||||
此示例以[瞬间插件](https://github.com/halo-sigs/plugin-moments)为例,目前瞬间插件中的评论是通过 Halo 的内置的评论功能实现的,所以需要扩展评论来源显示。
|
||||
|
||||
```ts
|
||||
import {
|
||||
definePlugin,
|
||||
type CommentSubjectRefResult,
|
||||
} from "@halo-dev/console-shared";
|
||||
import { markRaw } from "vue";
|
||||
import type { Moment } from "@/types";
|
||||
import { formatDatetime } from "./utils/date";
|
||||
import type { Extension } from "@halo-dev/api-client/index";
|
||||
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"comment:subject-ref:create": () => {
|
||||
return [
|
||||
{
|
||||
kind: "Moment",
|
||||
group: "moment.halo.run",
|
||||
resolve: (subject: Extension): CommentSubjectRefResult => {
|
||||
const moment = subject as Moment;
|
||||
return {
|
||||
label: "瞬间",
|
||||
title: determineMomentTitle(moment),
|
||||
externalUrl: `/moments/${moment.metadata.name}`,
|
||||
route: {
|
||||
name: "Moments",
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const determineMomentTitle = (moment: Moment) => {
|
||||
const pureContent = stripHtmlTags(moment.spec.content.raw);
|
||||
const title = !pureContent?.trim()
|
||||
? formatDatetime(new Date(moment.spec.releaseTime || ""))
|
||||
: pureContent;
|
||||
return title?.substring(0, 100);
|
||||
};
|
||||
|
||||
const stripHtmlTags = (str: string) => {
|
||||
// strip html tags
|
||||
const stripped = str?.replace(/<\/?[^>]+(>|$)/gi, "") || "";
|
||||
// strip newlines and collapse spaces
|
||||
return stripped.replace(/\n/g, " ").replace(/\s+/g, " ");
|
||||
};
|
||||
```
|
||||
|
||||
## 实现案例
|
||||
|
||||
- [https://github.com/halo-sigs/plugin-moments](https://github.com/halo-sigs/plugin-moments)
|
@@ -0,0 +1,479 @@
|
||||
---
|
||||
title: 默认编辑器
|
||||
description: 扩展默认编辑器 - default:editor:extension:create
|
||||
---
|
||||
|
||||
此扩展点用于扩展默认编辑器的功能。
|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"default:editor:extension:create": (): AnyExtension[] | Promise<AnyExtension[]> => {
|
||||
return [FooExtension];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
:::info 提示
|
||||
AnyExtension 类型来自 [Tiptap](https://github.com/ueberdosis/tiptap),这意味着 Halo 默认编辑器的扩展点返回类型与 Tiptap 的扩展完全一致,Tiptap 的扩展文档可参考:[https://tiptap.dev/docs/editor/api/extensions](https://tiptap.dev/docs/editor/api/extensions)。此外,Halo 也为默认编辑器的扩展提供了一些独有的参数,用于实现工具栏、指令等扩展。
|
||||
:::
|
||||
|
||||
### Halo 独有扩展
|
||||
|
||||
阅读此文当前请确保已经熟悉了 Tiptap 的扩展文档,这里将介绍如何对编辑器的功能进行扩展,包括但不限于扩展工具栏、悬浮工具栏、Slash Command、拖拽功能等。
|
||||
|
||||
目前支持的所有扩展类型如下所示:
|
||||
|
||||
```ts
|
||||
export interface ExtensionOptions {
|
||||
// 顶部工具栏扩展
|
||||
getToolbarItems?: ({
|
||||
editor,
|
||||
}: {
|
||||
editor: Editor;
|
||||
}) => ToolbarItem | ToolbarItem[];
|
||||
|
||||
// Slash Command 扩展
|
||||
getCommandMenuItems?: () => CommandMenuItem | CommandMenuItem[];
|
||||
|
||||
// 悬浮菜单扩展
|
||||
getBubbleMenu?: ({ editor }: { editor: Editor }) => NodeBubbleMenu;
|
||||
|
||||
// 工具箱扩展
|
||||
getToolboxItems?: ({
|
||||
editor,
|
||||
}: {
|
||||
editor: Editor;
|
||||
}) => ToolboxItem | ToolboxItem[];
|
||||
|
||||
// 拖拽扩展
|
||||
getDraggable?: ({ editor }: { editor: Editor }) => DraggableItem | boolean;
|
||||
}
|
||||
```
|
||||
|
||||
#### 1. 顶部工具栏扩展
|
||||
|
||||
编辑器顶部功能区域内容的扩展,通常用于增加用户常用操作,例如文本加粗、变更颜色等。
|
||||
|
||||

|
||||
|
||||
在 [https://github.com/halo-sigs/richtext-editor/pull/16](https://github.com/halo-sigs/richtext-editor/pull/16) 中,我们实现了对顶部工具栏的扩展,如果需要添加额外的功能,只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getToolbarItems` 函数即可,如:
|
||||
|
||||
```ts
|
||||
{
|
||||
addOptions() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
getToolbarItems({ editor }: { editor: Editor }) {
|
||||
return []
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
其中 `getToolbarItems` 即为对顶部工具栏的扩展。其返回类型为:
|
||||
|
||||
```ts
|
||||
// 顶部工具栏扩展
|
||||
getToolbarItems?: ({
|
||||
editor,
|
||||
}: {
|
||||
editor: Editor;
|
||||
}) => ToolbarItem | ToolbarItem[];
|
||||
|
||||
// 工具栏
|
||||
export interface ToolbarItem {
|
||||
priority: number;
|
||||
component: Component;
|
||||
props: {
|
||||
editor: Editor;
|
||||
isActive: boolean;
|
||||
disabled?: boolean;
|
||||
icon?: Component;
|
||||
title?: string;
|
||||
action?: () => void;
|
||||
};
|
||||
children?: ToolbarItem[];
|
||||
}
|
||||
```
|
||||
|
||||
如下为 [`Bold`](https://github.com/halo-dev/halo/blob/main/console/packages/editor/src/extensions/bold/index.ts) 扩展中对于 `getToolbarItems` 的扩展示例:
|
||||
|
||||
```ts
|
||||
addOptions() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
getToolbarItems({ editor }: { editor: Editor }) {
|
||||
return {
|
||||
priority: 40,
|
||||
component: markRaw(ToolbarItem),
|
||||
props: {
|
||||
editor,
|
||||
isActive: editor.isActive("bold"),
|
||||
icon: markRaw(MdiFormatBold),
|
||||
title: i18n.global.t("editor.common.bold"),
|
||||
action: () => editor.chain().focus().toggleBold().run(),
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
```
|
||||
|
||||
#### 2. 工具箱扩展
|
||||
|
||||
编辑器工具箱区域的扩展,可用于增加编辑器附属操作,例如插入表格,插入第三方组件等功能。
|
||||
|
||||

|
||||
|
||||
在 [https://github.com/halo-sigs/richtext-editor/pull/27](https://github.com/halo-sigs/richtext-editor/pull/27) 中,我们实现了对编辑器工具箱区域的扩展,如果需要添加额外的功能,只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getToolboxItems` 函数即可,如:
|
||||
|
||||
```ts
|
||||
{
|
||||
addOptions() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
getToolboxItems({ editor }: { editor: Editor }) {
|
||||
return []
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
其中 `getToolboxItems` 即为对工具箱的扩展。其返回类型为:
|
||||
|
||||
```ts
|
||||
// 工具箱扩展
|
||||
getToolboxItems?: ({
|
||||
editor,
|
||||
}: {
|
||||
editor: Editor;
|
||||
}) => ToolboxItem | ToolboxItem[];
|
||||
|
||||
export interface ToolboxItem {
|
||||
priority: number;
|
||||
component: Component;
|
||||
props: {
|
||||
editor: Editor;
|
||||
icon?: Component;
|
||||
title?: string;
|
||||
description?: string;
|
||||
action?: () => void;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
如下为 [`Table`](https://github.com/halo-dev/halo/blob/main/console/packages/editor/src/extensions/table/index.ts) 扩展中对于 `getToolboxItems` 工具箱的扩展示例:
|
||||
|
||||
```ts
|
||||
addOptions() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
getToolboxItems({ editor }: { editor: Editor }) {
|
||||
return {
|
||||
priority: 15,
|
||||
component: markRaw(ToolboxItem),
|
||||
props: {
|
||||
editor,
|
||||
icon: markRaw(MdiTablePlus),
|
||||
title: i18n.global.t("editor.menus.table.add"),
|
||||
action: () =>
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.insertTable({ rows: 3, cols: 3, withHeaderRow: true })
|
||||
.run(),
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Slash Command 扩展
|
||||
|
||||
Slash Command (斜杠命令)的扩展,可用于在当前行快捷执行功能操作,例如转换当前行为标题、在当前行添加代码块等功能。
|
||||
|
||||

|
||||
|
||||
在 [https://github.com/halo-sigs/richtext-editor/pull/16](https://github.com/halo-sigs/richtext-editor/pull/16) 中,我们实现了对 Slash Command 指令的扩展,如果需要添加额外的功能,只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getCommandMenuItems` 函数即可,如:
|
||||
|
||||
```ts
|
||||
{
|
||||
addOptions() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
getCommandMenuItems() {
|
||||
return []
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
其中 `getCommandMenuItems` 即为对工具箱的扩展。其返回类型为:
|
||||
|
||||
```ts
|
||||
// Slash Command 扩展
|
||||
getCommandMenuItems?: () => CommandMenuItem | CommandMenuItem[];
|
||||
|
||||
export interface CommandMenuItem {
|
||||
priority: number;
|
||||
icon: Component;
|
||||
title: string;
|
||||
keywords: string[];
|
||||
command: ({ editor, range }: { editor: Editor; range: Range }) => void;
|
||||
}
|
||||
```
|
||||
|
||||
如下为 [`Table`](https://github.com/halo-dev/halo/blob/main/console/packages/editor/src/extensions/table/index.ts) 扩展中对于 `getCommandMenuItems` 的扩展示例:
|
||||
|
||||
```ts
|
||||
addOptions() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
getCommandMenuItems() {
|
||||
return {
|
||||
priority: 120,
|
||||
icon: markRaw(MdiTable),
|
||||
title: "editor.extensions.commands_menu.table",
|
||||
keywords: ["table", "biaoge"],
|
||||
command: ({ editor, range }: { editor: Editor; range: Range }) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange(range)
|
||||
.insertTable({ rows: 3, cols: 3, withHeaderRow: true })
|
||||
.run();
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 悬浮菜单扩展
|
||||
|
||||
编辑器悬浮菜单的扩展。可用于支持目标元素组件的功能扩展及操作简化。例如 `Table` 扩展中的添加下一列、添加上一列等操作。
|
||||
|
||||

|
||||
|
||||
在 [https://github.com/halo-sigs/richtext-editor/pull/38](https://github.com/halo-sigs/richtext-editor/pull/38) 中,我们重构了对编辑器悬浮区域的扩展,如果需要对某个块进行支持,只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getBubbleMenu` 函数即可,如:
|
||||
|
||||
```ts
|
||||
{
|
||||
addOptions() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
getBubbleMenu({ editor }: { editor: Editor }) {
|
||||
return []
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
其中 `getBubbleMenu` 即为对悬浮菜单的扩展。其返回类型为:
|
||||
|
||||
```ts
|
||||
// 悬浮菜单扩展
|
||||
getBubbleMenu?: ({ editor }: { editor: Editor }) => NodeBubbleMenu;
|
||||
|
||||
interface BubbleMenuProps {
|
||||
pluginKey?: string; // 悬浮菜单插件 Key,建议命名方式 xxxBubbleMenu
|
||||
editor?: Editor;
|
||||
shouldShow: (props: { // 悬浮菜单显示的条件
|
||||
editor: Editor;
|
||||
node?: HTMLElement;
|
||||
view?: EditorView;
|
||||
state?: EditorState;
|
||||
oldState?: EditorState;
|
||||
from?: number;
|
||||
to?: number;
|
||||
}) => boolean;
|
||||
tippyOptions?: Record\<string, unknown\>; // 可自由定制悬浮菜单所用的 tippy 组件的选项
|
||||
getRenderContainer?: (node: HTMLElement) => HTMLElement; // 悬浮菜单所基准的 DOM
|
||||
defaultAnimation?: boolean; // 是否启用默认动画。默认为 true
|
||||
}
|
||||
|
||||
// 悬浮菜单
|
||||
export interface NodeBubbleMenu extends BubbleMenuProps {
|
||||
component?: Component; // 不使用默认的样式,与 items 二选一
|
||||
items?: BubbleItem[]; // 悬浮菜单子项,使用默认的形式进行,与 items 二选一
|
||||
}
|
||||
|
||||
// 悬浮菜单子项
|
||||
export interface BubbleItem {
|
||||
priority: number; // 优先级,数字越小优先级越大,越靠前
|
||||
component?: Component; // 完全自定义子项样式
|
||||
props: {
|
||||
isActive: ({ editor }: { editor: Editor }) => boolean; // 当前功能是否已经处于活动状态
|
||||
visible?: ({ editor }: { editor: Editor }) => boolean; // 是否显示当前子项
|
||||
icon?: Component; // 图标
|
||||
iconStyle?: string; // 图标自定义样式
|
||||
title?: string; // 标题
|
||||
action?: ({ editor }: { editor: Editor }) => Component | void; // 点击子项后的操作,如果返回 Component,则会将其包含在下拉框中。
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
如下为 [`Table`](https://github.com/halo-dev/halo/blob/main/console/packages/editor/src/extensions/table/index.ts) 扩展中对于 `getBubbleMenu` 悬浮菜单的部分扩展示例:
|
||||
|
||||
```ts
|
||||
addOptions() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
getBubbleMenu({ editor }) {
|
||||
return {
|
||||
pluginKey: "tableBubbleMenu",
|
||||
shouldShow: ({ state }: { state: EditorState }): boolean => {
|
||||
return isActive(state, Table.name);
|
||||
},
|
||||
getRenderContainer(node) {
|
||||
let container = node;
|
||||
if (container.nodeName === "#text") {
|
||||
container = node.parentElement as HTMLElement;
|
||||
}
|
||||
while (
|
||||
container &&
|
||||
container.classList &&
|
||||
!container.classList.contains("tableWrapper")
|
||||
) {
|
||||
container = container.parentElement as HTMLElement;
|
||||
}
|
||||
return container;
|
||||
},
|
||||
tippyOptions: {
|
||||
offset: [26, 0],
|
||||
},
|
||||
items: [
|
||||
{
|
||||
priority: 10,
|
||||
props: {
|
||||
icon: markRaw(MdiTableColumnPlusBefore),
|
||||
title: i18n.global.t("editor.menus.table.add_column_before"),
|
||||
action: () => editor.chain().focus().addColumnBefore().run(),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 5. 拖拽功能扩展
|
||||
|
||||
拖拽功能的扩展,可用于支持当前块元素的拖拽功能。
|
||||
|
||||

|
||||
|
||||
在 [https://github.com/halo-sigs/richtext-editor/pull/48](https://github.com/halo-sigs/richtext-editor/pull/48) 中,我们实现了对所有元素的拖拽功能,如果需要让当前扩展支持拖拽,只需要在具体的 Tiptap Extension 中的 `addOptions` 中定义 `getDraggable` 函数,并让其返回 true 即可。如:
|
||||
|
||||
```ts
|
||||
{
|
||||
addOptions() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
getDraggable() {
|
||||
return true;
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
其中 `getDraggable` 即为为当前扩展增加可拖拽的功能。其返回类型为:
|
||||
|
||||
```ts
|
||||
// 拖拽扩展
|
||||
getDraggable?: ({ editor }: { editor: Editor }) => DraggableItem | boolean;
|
||||
|
||||
export interface DraggableItem {
|
||||
getRenderContainer?: ({ // 拖拽按钮计算偏移位置的基准 DOM
|
||||
dom,
|
||||
view,
|
||||
}: {
|
||||
dom: HTMLElement;
|
||||
view: EditorView;
|
||||
}) => DragSelectionNode;
|
||||
handleDrop?: ({ // 完成拖拽功能之后的处理。返回 true 则会阻止拖拽的发生
|
||||
view,
|
||||
event,
|
||||
slice,
|
||||
insertPos,
|
||||
node,
|
||||
selection,
|
||||
}: {
|
||||
view: EditorView;
|
||||
event: DragEvent;
|
||||
slice: Slice;
|
||||
insertPos: number;
|
||||
node: Node;
|
||||
selection: Selection;
|
||||
}) => boolean | void;
|
||||
allowPropagationDownward?: boolean; // 是否允许拖拽事件向内部传播,
|
||||
}
|
||||
|
||||
export interface DragSelectionNode {
|
||||
$pos?: ResolvedPos;
|
||||
node?: Node;
|
||||
el: HTMLElement;
|
||||
nodeOffset?: number;
|
||||
dragDomOffset?: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
> 拖拽会从父 Node 节点开始触发,直到找到一个实现 `getDraggable` 的扩展,如果没有找到,则不会触发拖拽事件。父 Node 可以通过 `allowPropagationDownward` 来控制是否允许拖拽事件向内部传播。如果 `allowPropagationDownward` 设置为 true,则会继续向内部寻找实现 `getDraggable` 的扩展,如果没有找到,则触发父 Node 的 `getDraggable` 实现,否则继续进行传播。
|
||||
|
||||
如下为 [`Iframe`](https://github.com/halo-dev/halo/blob/main/console/packages/editor/src/extensions/iframe/index.ts) 扩展中对于 `getDraggable` 拖拽功能的扩展示例:
|
||||
|
||||
```ts
|
||||
addOptions() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
getDraggable() {
|
||||
return {
|
||||
getRenderContainer({ dom, view }) {
|
||||
let container = dom;
|
||||
while (
|
||||
container.parentElement &&
|
||||
container.parentElement.tagName !== "P"
|
||||
) {
|
||||
container = container.parentElement;
|
||||
}
|
||||
if (container) {
|
||||
container = container.firstElementChild
|
||||
?.firstElementChild as HTMLElement;
|
||||
}
|
||||
let node;
|
||||
if (container.firstElementChild) {
|
||||
const pos = view.posAtDOM(container.firstElementChild, 0);
|
||||
const $pos = view.state.doc.resolve(pos);
|
||||
node = $pos.node();
|
||||
}
|
||||
|
||||
return {
|
||||
node: node,
|
||||
el: container as HTMLElement,
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 实现案例
|
||||
|
||||
- [https://github.com/halo-sigs/plugin-hybrid-edit-block](https://github.com/halo-sigs/plugin-hybrid-edit-block)
|
||||
- [https://github.com/halo-sigs/plugin-katex](https://github.com/halo-sigs/plugin-katex)
|
||||
- [https://github.com/halo-sigs/plugin-text-diagram](https://github.com/halo-sigs/plugin-text-diagram)
|
@@ -0,0 +1,188 @@
|
||||
---
|
||||
title: 编辑器集成
|
||||
description: 通过实现扩展点为文章提供新的编辑器 - editor:create
|
||||
---
|
||||
|
||||
此扩展点可以为文章提供新的独立编辑器。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"editor:create": (): EditorProvider[] | Promise<EditorProvider[]> => {
|
||||
return [
|
||||
{
|
||||
name: "foo",
|
||||
displayName: "foo",
|
||||
logo: "/plugins/plugin-foo/assets/logo.png",
|
||||
component: FooComponent,
|
||||
rawType: "markdown",
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts title="EditorProvider"
|
||||
export interface EditorProvider {
|
||||
name: string;
|
||||
displayName: string;
|
||||
logo?: string;
|
||||
component: Component;
|
||||
rawType: string;
|
||||
}
|
||||
```
|
||||
|
||||
其中,`component` 可以是组件对象或组件名称,且此组件有以下实现要求:
|
||||
|
||||
1. 组件包含以下 props:
|
||||
1. `title:string`:用于接收标题。
|
||||
2. `raw:string`:用于接收原始内容。
|
||||
3. `content:string`:用于接收渲染后的内容。
|
||||
4. `uploadImage?: (file: File) => Promise<Attachment>`:用于上传图片,在编辑器内部获取到 File 之后直接调用此方法即可得到上传后的附件信息。
|
||||
2. 组件包含以下 emit 事件:
|
||||
1. `update:title`:用于更新标题。
|
||||
2. `update:raw`:用于更新原始内容。
|
||||
3. `update:content`:用于更新渲染后的内容。
|
||||
4. `update`:发送更新事件。
|
||||
|
||||
## 示例
|
||||
|
||||
此示例将实现一个简单的 Markdown 编辑器。
|
||||
|
||||
```ts title="index.ts"
|
||||
import { definePlugin } from "@halo-dev/console-shared";
|
||||
import { markRaw } from "vue";
|
||||
import MarkdownEditor from "./components/markdown-editor.vue";
|
||||
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"editor:create": () => {
|
||||
return [
|
||||
{
|
||||
name: "markdown",
|
||||
displayName: "Markdown 编辑器",
|
||||
component: markRaw(MarkdownEditor),
|
||||
rawType: "markdown",
|
||||
logo: "/plugins/markdown-editor/assets/logo.png",
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```html title="./components/markdown-editor.vue"
|
||||
<script setup lang="ts">
|
||||
import { marked } from "marked";
|
||||
import { debounce } from "lodash-es";
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import type { Attachment } from "@halo-dev/api-client";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
raw: string;
|
||||
content: string;
|
||||
uploadImage?: (file: File) => Promise<Attachment>;
|
||||
}>(),
|
||||
{
|
||||
raw: "",
|
||||
content: "",
|
||||
uploadImage: undefined,
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: "update:raw", value: string): void;
|
||||
(event: "update:content", value: string): void;
|
||||
(event: "update", value: string): void;
|
||||
}>();
|
||||
|
||||
const output = computed(() => marked(props.raw));
|
||||
|
||||
const update = debounce((e) => {
|
||||
emit("update:raw", e.target.value);
|
||||
emit("update:content", marked(e.target.value));
|
||||
|
||||
if (e.target.value !== props.raw) {
|
||||
emit("update", e.target.value);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
const textareaRef = ref();
|
||||
|
||||
onMounted(() => {
|
||||
textareaRef.value.addEventListener("paste", (e) => {
|
||||
if (e.clipboardData && e.clipboardData.items) {
|
||||
const items = e.clipboardData.items;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i].type.indexOf("image") !== -1) {
|
||||
const file = items[i].getAsFile();
|
||||
props.uploadImage?.(file).then((attachment: Attachment) => {
|
||||
emit(
|
||||
"update:raw",
|
||||
props.raw +
|
||||
``
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="editor">
|
||||
<textarea ref="textareaRef" class="input" :value="raw" @input="update"></textarea>
|
||||
<div class="output" v-html="output"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.editor {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.input,
|
||||
.output {
|
||||
overflow: auto;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.input {
|
||||
border: none;
|
||||
border-right: 1px solid #ccc;
|
||||
resize: none;
|
||||
outline: none;
|
||||
background-color: #f6f6f6;
|
||||
font-size: 14px;
|
||||
font-family: 'Monaco', courier, monospace;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
code {
|
||||
color: #f66;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
> 来源:[https://vuejs.org/examples/#markdown](https://vuejs.org/examples/#markdown)
|
||||
|
||||
## 实现案例
|
||||
|
||||
- [https://github.com/halo-sigs/plugin-stackedit](https://github.com/halo-sigs/plugin-stackedit)
|
||||
- [https://github.com/halo-sigs/plugin-bytemd](https://github.com/halo-sigs/plugin-bytemd)
|
||||
- [https://github.com/justice2001/halo-plugin-vditor](https://github.com/justice2001/halo-plugin-vditor)
|
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: 扩展点
|
||||
description: Halo UI 为插件提供的扩展点接口
|
||||
---
|
||||
|
||||
UI 扩展点是用于扩展 Console 和 UC 的界面的接口,通过实现扩展点接口,插件可以在 Console 和 UC 中扩展功能。
|
||||
|
||||
以下是目前已支持的扩展点列表:
|
||||
|
||||
```mdx-code-block
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
|
||||
<DocCardList />
|
||||
```
|
@@ -0,0 +1,25 @@
|
||||
```ts
|
||||
export interface Attachment {
|
||||
apiVersion: "storage.halo.run/v1alpha1"
|
||||
kind: "Attachment"
|
||||
metadata: {
|
||||
annotations: {}
|
||||
creationTimestamp: string
|
||||
labels: {}
|
||||
name: string // 附件的唯一标识
|
||||
version: number
|
||||
}
|
||||
spec: {
|
||||
displayName: string // 附件名称
|
||||
groupName: string // 附件分组
|
||||
mediaType: string // 附件类型
|
||||
ownerName: string // 附件上传者
|
||||
policyName: string // 附件存储策略
|
||||
size: number // 附件大小
|
||||
tags: Array<string>
|
||||
}
|
||||
status: {
|
||||
permalink: string // 附件固定访问地址
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,64 @@
|
||||
```ts
|
||||
export interface ListedComment {
|
||||
comment:{
|
||||
apiVersion: "content.halo.run/v1alpha1"
|
||||
kind: "Comment"
|
||||
metadata: {
|
||||
annotations: {}
|
||||
creationTimestamp: string
|
||||
labels: {}
|
||||
name: string // 评论的唯一标识
|
||||
version: number
|
||||
}
|
||||
spec: {
|
||||
allowNotification: boolean; // 是否允许通知
|
||||
approved: boolean;
|
||||
approvedTime: string;
|
||||
content: string; // 最终渲染的文本
|
||||
creationTime: string; // 创建时间
|
||||
hidden: boolean;
|
||||
ipAddress: string; // 评论者 IP 地址
|
||||
lastReadTime: string;
|
||||
owner: { // 创建者信息
|
||||
annotations: {};
|
||||
displayName: string;
|
||||
kind: string;
|
||||
name: string;
|
||||
};
|
||||
priority: number; // 排序字段
|
||||
raw: string; // 原始文本,一般用于给编辑器使用
|
||||
subjectRef: { // 引用关联,比如文章、自定义页面
|
||||
group: string;
|
||||
kind: string;
|
||||
name: string;
|
||||
version: string;
|
||||
};
|
||||
top: boolean; // 是否置顶
|
||||
userAgent: string; // 评论者 UserAgent 信息
|
||||
}
|
||||
status: {
|
||||
hasNewReply: boolean; // 是否有新回复
|
||||
lastReplyTime: string;
|
||||
observedVersion: number;
|
||||
replyCount: number; // 回复数量
|
||||
unreadReplyCount: number;
|
||||
visibleReplyCount: number;
|
||||
}
|
||||
}
|
||||
owner: { // 创建者信息
|
||||
avatar: string; // 头像
|
||||
displayName: string; // 描述
|
||||
email: string; // 邮箱
|
||||
kind: string;
|
||||
name: string; // 创建者的唯一标识
|
||||
}
|
||||
stats: {
|
||||
upvote: number;
|
||||
}
|
||||
subject: {
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
metadata: Metadata;
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,119 @@
|
||||
```ts
|
||||
export interface ListedPost {
|
||||
categories: Array<{ // 文章的分类集合
|
||||
apiVersion: "content.halo.run/v1alpha1";
|
||||
kind: "Category";
|
||||
metadata: {
|
||||
annotations: {};
|
||||
creationTimestamp: string;
|
||||
labels: {};
|
||||
name: string; // 分类的唯一标识
|
||||
version: number;
|
||||
};
|
||||
spec: {
|
||||
children: Array<string>; // 子分类
|
||||
cover: string; // 分类封面图
|
||||
description: string; // 分类描述
|
||||
displayName: string; // 分类名称
|
||||
priority: number; // 分类优先级
|
||||
slug: string; // 分类别名
|
||||
template: string; // 分类渲染模板
|
||||
};
|
||||
status: {
|
||||
permalink: string; // 分类的永久链接
|
||||
postCount: number; // 分类下的文章总数
|
||||
visiblePostCount: number; // 分类下可见的文章总数
|
||||
};
|
||||
}>;
|
||||
contributors: Array<{ // 文章的贡献者集合
|
||||
avatar: string; // 贡献者头像
|
||||
displayName: string; // 贡献者名称
|
||||
name: string; // 贡献者唯一标识
|
||||
}>;
|
||||
owner: { // 文章的作者信息
|
||||
avatar: string; // 作者头像
|
||||
displayName: string; // 作者名称
|
||||
name: string; // 作者唯一标识
|
||||
};
|
||||
post: { // 文章信息
|
||||
apiVersion: "content.halo.run/v1alpha1";
|
||||
kind: "Post";
|
||||
metadata: {
|
||||
annotations: {};
|
||||
creationTimestamp: string;
|
||||
labels: {};
|
||||
name: string; // 文章的唯一标识
|
||||
version: number;
|
||||
};
|
||||
spec: {
|
||||
allowComment: boolean; // 是否允许评论
|
||||
baseSnapshot: string; // 内容基础快照
|
||||
categories: Array<string>; // 文章所属分类
|
||||
cover: string; // 文章封面图
|
||||
deleted: boolean; // 是否已删除
|
||||
excerpt: { // 文章摘要
|
||||
autoGenerate: boolean; // 是否自动生成
|
||||
raw: string; // 摘要内容
|
||||
};
|
||||
headSnapshot: string; // 内容最新快照
|
||||
htmlMetas: Array<{}>;
|
||||
owner: string; // 文章作者的唯一标识
|
||||
pinned: boolean; // 是否置顶
|
||||
priority: number; // 文章优先级
|
||||
publish: boolean; // 是否发布
|
||||
publishTime: string; // 发布时间
|
||||
releaseSnapshot: string; // 已发布的内容快照
|
||||
slug: string; // 文章别名
|
||||
tags: Array<string>; // 文章所属标签
|
||||
template: string; // 文章渲染模板
|
||||
title: string; // 文章标题
|
||||
visible: string; // 文章可见性
|
||||
};
|
||||
status: {
|
||||
commentsCount: number; // 文章评论总数
|
||||
conditions: Array<{
|
||||
lastTransitionTime: string;
|
||||
message: string;
|
||||
reason: string;
|
||||
status: string;
|
||||
type: string;
|
||||
}>;
|
||||
contributors: Array<string>;
|
||||
excerpt: string; // 最终的文章摘要,根据是否自动生成计算
|
||||
inProgress: boolean; // 是否有未发布的内容
|
||||
lastModifyTime: string; // 文章最后修改时间
|
||||
permalink: string; // 文章的永久链接
|
||||
phase: string;
|
||||
};
|
||||
};
|
||||
stats: {
|
||||
approvedComment: number; // 已审核的评论数
|
||||
totalComment: number; // 评论总数
|
||||
upvote: number; // 点赞数
|
||||
visit: number; // 访问数
|
||||
};
|
||||
tags: Array<{ // 文章的标签集合
|
||||
apiVersion: "content.halo.run/v1alpha1";
|
||||
kind: "Tag";
|
||||
metadata: {
|
||||
annotations: {};
|
||||
creationTimestamp: string;
|
||||
labels: {};
|
||||
name: string; // 标签的唯一标识
|
||||
version: number;
|
||||
};
|
||||
spec: {
|
||||
color: string; // 标签颜色
|
||||
cover: string; // 标签封面图
|
||||
displayName: string; // 标签名称
|
||||
slug: string; // 标签别名
|
||||
};
|
||||
status: {
|
||||
permalink: string; // 标签的永久链接
|
||||
postCount: number; // 标签下的文章总数
|
||||
visiblePostCount: number; // 标签下可见的文章总数
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
||||
```
|
@@ -0,0 +1,49 @@
|
||||
```ts
|
||||
export interface ListedReply {
|
||||
owner: { // 创建者信息
|
||||
avatar: string; // 头像
|
||||
displayName: string; // 描述
|
||||
email: string; // 邮箱
|
||||
kind: string;
|
||||
name: string; // 创建者的唯一标识
|
||||
}
|
||||
reply:{
|
||||
apiVersion: "content.halo.run/v1alpha1"
|
||||
kind: "Reply"
|
||||
metadata: {
|
||||
annotations: {}
|
||||
creationTimestamp: string
|
||||
labels: {}
|
||||
name: string // 评论的唯一标识
|
||||
version: number
|
||||
}
|
||||
spec: {
|
||||
allowNotification: boolean; // 是否允许通知
|
||||
approved: boolean;
|
||||
approvedTime: string;
|
||||
commentName: string; // 被回复的评论名称,即 Comment 的 metadata.name
|
||||
content: string; // 最终渲染的文本
|
||||
creationTime: string; // 创建时间
|
||||
hidden: boolean;
|
||||
ipAddress: string; // 评论者 IP 地址
|
||||
owner: { // 创建者信息
|
||||
annotations: {};
|
||||
displayName: string;
|
||||
kind: string;
|
||||
name: string;
|
||||
};
|
||||
priority: number; // 排序字段
|
||||
quoteReply: string; // 被回复的回复名称,即 Reply 的 metadata.name
|
||||
raw: string; // 原始文本,一般用于给编辑器使用
|
||||
top: boolean; // 是否置顶
|
||||
userAgent: string; // 评论者 UserAgent 信息
|
||||
}
|
||||
status: {
|
||||
observedVersion: number;
|
||||
}
|
||||
}
|
||||
stats: {
|
||||
upvote: number;
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,12 @@
|
||||
```ts title="OperationItem"
|
||||
export interface OperationItem<T> {
|
||||
priority: number; // 排序优先级
|
||||
component: Raw<Component>; // 菜单项组件
|
||||
props?: Record\<string, unknown\>; // 菜单项组件属性
|
||||
action?: (item?: T) => void; // 菜单项点击事件
|
||||
label?: string; // 菜单项标题
|
||||
hidden?: boolean; // 菜单项是否隐藏
|
||||
permissions?: string[]; // 菜单项 UI 权限
|
||||
children?: OperationItem<T>[]; // 子菜单项
|
||||
}
|
||||
```
|
@@ -0,0 +1,50 @@
|
||||
```ts
|
||||
export interface Plugin {
|
||||
apiVersion: "plugin.halo.run/v1alpha1"
|
||||
kind: "Plugin"
|
||||
metadata: {
|
||||
annotations: {}
|
||||
creationTimestamp: string // 创建时间
|
||||
labels: {}
|
||||
name: string // 唯一标识
|
||||
version: number
|
||||
}
|
||||
spec: {
|
||||
author: { // 作者信息
|
||||
name: string
|
||||
website: string
|
||||
}
|
||||
configMapName: string // 关联的 ConfigMap 模型,用于存储配置
|
||||
description: string // 插件描述
|
||||
displayName: string // 插件名称
|
||||
enabled: boolean
|
||||
homepage: string // 插件主页
|
||||
license: Array<{ // 插件协议
|
||||
name: string
|
||||
url: string
|
||||
}>
|
||||
logo: string
|
||||
pluginDependencies: {}
|
||||
repo: string // 插件仓库地址
|
||||
requires: string // 所依赖的 Halo 版本
|
||||
settingName: string // 关联的 Setting 模型,用于渲染配置表单
|
||||
version: string // 插件版本
|
||||
}
|
||||
status: {
|
||||
conditions: Array<{
|
||||
lastTransitionTime: string
|
||||
message: string
|
||||
reason: string
|
||||
status: string
|
||||
type: string
|
||||
}>
|
||||
entry: string
|
||||
lastProbeState: string
|
||||
lastStartTime: string
|
||||
loadLocation: string
|
||||
logo: string // 插件 Logo 地址
|
||||
phase: string
|
||||
stylesheet: string
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,63 @@
|
||||
```ts
|
||||
export interface Theme {
|
||||
apiVersion: "theme.halo.run/v1alpha1"
|
||||
kind: "Theme"
|
||||
metadata: {
|
||||
annotations: {}
|
||||
creationTimestamp: string
|
||||
labels: {}
|
||||
name: string // 主题的唯一标识
|
||||
version: number
|
||||
}
|
||||
spec: {
|
||||
author: { // 主题作者信息
|
||||
name: string
|
||||
website: string
|
||||
}
|
||||
configMapName: string // 关联的 ConfigMap 模型,用于存储配置
|
||||
customTemplates: { // 自定义模板信息
|
||||
category: Array<{
|
||||
description: string
|
||||
file: string
|
||||
name: string
|
||||
screenshot: string
|
||||
}>
|
||||
page: Array<{
|
||||
description: string
|
||||
file: string
|
||||
name: string
|
||||
screenshot: string
|
||||
}>
|
||||
post: Array<{
|
||||
description: string
|
||||
file: string
|
||||
name: string
|
||||
screenshot: string
|
||||
}>
|
||||
}
|
||||
description: string // 主题描述
|
||||
displayName: string // 主题名称
|
||||
homepage: string // 主题主页
|
||||
license: Array<{ // 主题许可证信息
|
||||
name: string
|
||||
url: string
|
||||
}>
|
||||
logo: string // 主题 Logo
|
||||
repo: string // 主题仓库地址
|
||||
requires: string // 所依赖的 Halo 版本
|
||||
settingName: string // 关联的 Setting 模型,用于渲染配置表单
|
||||
version: string // 主题版本
|
||||
}
|
||||
status: {
|
||||
conditions: Array<{
|
||||
lastTransitionTime: string
|
||||
message: string
|
||||
reason: string
|
||||
status: string
|
||||
type: string
|
||||
}>
|
||||
location: string
|
||||
phase: string
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: 插件安装界面选项卡
|
||||
description: 扩展插件安装界面选项卡 - plugin:installation:tabs:create
|
||||
---
|
||||
|
||||
目前 Halo 原生支持本地上传和远程下载的方式安装插件,此扩展点用于扩展插件安装界面的选项卡,以支持更多的安装方式。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"plugin:installation:tabs:create": (): PluginInstallationTab[] | Promise<PluginInstallationTab[]> => {
|
||||
return [
|
||||
{
|
||||
id: "foo",
|
||||
label: "foo",
|
||||
component: markRaw(FooComponent),
|
||||
props: {},
|
||||
permissions: [],
|
||||
priority: 0,
|
||||
}
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts title="PluginInstallationTab"
|
||||
export interface PluginInstallationTab {
|
||||
id: string; // 选项卡 ID
|
||||
label: string; // 选项卡标题
|
||||
component: Raw<Component>; // 选项卡面板组件
|
||||
props?: Record\<string, unknown\>; // 选项卡面板组件属性
|
||||
permissions?: string[]; // 选项卡 UI 权限
|
||||
priority: number; // 选项卡排序优先级
|
||||
}
|
||||
```
|
||||
|
||||
## 实现案例
|
||||
|
||||
- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store)
|
@@ -0,0 +1,84 @@
|
||||
---
|
||||
title: 插件数据列表显示字段
|
||||
description: 扩展插件数据列表显示字段 - plugin:list-item:field:create
|
||||
---
|
||||
|
||||
此扩展点用于扩展插件数据列表的显示字段。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"plugin:list-item:field:create": (plugin: Ref<Plugin>): EntityFieldItem[] | Promise<EntityFieldItem[]> => {
|
||||
return [
|
||||
{
|
||||
priority: 0,
|
||||
position: "start",
|
||||
component: markRaw(FooComponent),
|
||||
props: {},
|
||||
permissions: [],
|
||||
hidden: false,
|
||||
}
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts title="EntityFieldItem"
|
||||
export interface EntityFieldItem {
|
||||
priority: number;
|
||||
position: "start" | "end";
|
||||
component: Raw<Component>;
|
||||
props?: Record\<string, unknown\>;
|
||||
permissions?: string[];
|
||||
hidden?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
## 示例
|
||||
|
||||
此示例将添加一个显示插件 requires(版本要求)的字段。
|
||||
|
||||
```ts
|
||||
import { definePlugin } from "@halo-dev/console-shared";
|
||||
import { markRaw, type Ref } from "vue";
|
||||
import type { Plugin } from "@halo-dev/api-client";
|
||||
import { VEntityField } from "@halo-dev/components";
|
||||
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"plugin:list-item:field:create": (plugin: Ref<Plugin>) => {
|
||||
return [
|
||||
{
|
||||
priority: 0,
|
||||
position: "end",
|
||||
component: markRaw(VEntityField),
|
||||
props: {
|
||||
description: plugin.value.spec.requires,
|
||||
},
|
||||
permissions: [],
|
||||
hidden: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 实现案例
|
||||
|
||||
- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store)
|
||||
|
||||
## 类型定义
|
||||
|
||||
### Plugin
|
||||
|
||||
```mdx-code-block
|
||||
import Plugin from "./interface/Plugin.md";
|
||||
|
||||
<Plugin />
|
||||
```
|
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: 插件数据列表操作菜单
|
||||
description: 扩展插件数据列表操作菜单 - plugin:list-item:operation:create
|
||||
---
|
||||
|
||||
此扩展点用于扩展插件数据列表的操作菜单项。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"plugin:list-item:operation:create": (
|
||||
plugin: Ref<Plugin>
|
||||
): OperationItem<Plugin>[] | Promise<OperationItem<Plugin>[]> => {
|
||||
return [
|
||||
{
|
||||
priority: 10,
|
||||
component: markRaw(VDropdownItem),
|
||||
props: {},
|
||||
action: (item?: Plugin) => {
|
||||
// do something
|
||||
},
|
||||
label: "foo",
|
||||
hidden: false,
|
||||
permissions: [],
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```mdx-code-block
|
||||
import OperationItem from "./interface/OperationItem.md";
|
||||
|
||||
<OperationItem />
|
||||
```
|
||||
|
||||
## 实现案例
|
||||
|
||||
- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store)
|
||||
|
||||
## 类型定义
|
||||
|
||||
### Plugin
|
||||
|
||||
```mdx-code-block
|
||||
import Plugin from "./interface/Plugin.md";
|
||||
|
||||
<Plugin />
|
||||
```
|
@@ -0,0 +1,90 @@
|
||||
---
|
||||
title: 插件详情选项卡
|
||||
description: 扩展当前插件的详情选项卡 - plugin:self:tabs:create
|
||||
---
|
||||
|
||||
此扩展点用于在 Console 的插件详情页面中添加自定义选项卡,可以用于自定义插件的配置页面。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"plugin:self:tabs:create": (): PluginTab[] | Promise<PluginTab[]> => {
|
||||
return [
|
||||
{
|
||||
id: "foo",
|
||||
label: "foo",
|
||||
component: markRaw(FooComponent),
|
||||
permissions: [],
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts title="PluginTab"
|
||||
export interface PluginTab {
|
||||
id: string; // 选项卡 ID,不能与设置表单的 group 重复
|
||||
label: string; // 选项卡标题
|
||||
component: Raw<Component>; // 选项卡面板组件
|
||||
permissions?: string[]; // 选项卡权限
|
||||
}
|
||||
```
|
||||
|
||||
其中,`component` 组件可以注入(inject)以下属性:
|
||||
|
||||
- `plugin`:当前插件对象,类型为 Ref\<[Plugin](#plugin)\>。
|
||||
|
||||
## 示例
|
||||
|
||||
此示例实现了一个自定义选项卡,用于获取插件的数据并显示名称。
|
||||
|
||||
```ts
|
||||
import { definePlugin, PluginTab } from "@halo-dev/console-shared";
|
||||
import MyComponent from "./views/my-component.vue";
|
||||
import { markRaw } from "vue";
|
||||
export default definePlugin({
|
||||
components: {},
|
||||
routes: [],
|
||||
extensionPoints: {
|
||||
"plugin:self:tabs:create": () : PluginTab[] => {
|
||||
return [
|
||||
{
|
||||
id: "my-tab-panel",
|
||||
label: "My Tab Panel",
|
||||
component: markRaw(MyComponent),
|
||||
permissions: []
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```html title="./views/my-component.vue"
|
||||
<script lang="ts" setup>
|
||||
const plugin = inject<Ref<Plugin | undefined>>("plugin");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ plugin?.spec.displayName }}</h1>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 实现案例
|
||||
|
||||
- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store)
|
||||
|
||||
## 类型定义
|
||||
|
||||
### Plugin
|
||||
|
||||
```mdx-code-block
|
||||
import Plugin from "./interface/Plugin.md";
|
||||
|
||||
<Plugin />
|
||||
```
|
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: 文章数据列表显示字段
|
||||
description: 扩展文章数据列表显示字段 - plugin:list-item:field:create
|
||||
---
|
||||
|
||||
此扩展点用于扩展文章数据列表的显示字段。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"post:list-item:field:create": (post: Ref<ListedPost>): EntityFieldItem[] | Promise<EntityFieldItem[]> => {
|
||||
return [
|
||||
{
|
||||
priority: 0,
|
||||
position: "start",
|
||||
component: markRaw(FooComponent),
|
||||
props: {},
|
||||
permissions: [],
|
||||
hidden: false,
|
||||
}
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts title="EntityFieldItem"
|
||||
export interface EntityFieldItem {
|
||||
priority: number;
|
||||
position: "start" | "end";
|
||||
component: Raw<Component>;
|
||||
props?: Record\<string, unknown\>;
|
||||
permissions?: string[];
|
||||
hidden?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
## 示例
|
||||
|
||||
此示例将添加一个显示文章 slug(别名)的字段。
|
||||
|
||||
```ts
|
||||
import { definePlugin } from "@halo-dev/console-shared";
|
||||
import { markRaw, type Ref } from "vue";
|
||||
import type { ListedPost } from "@halo-dev/api-client";
|
||||
import { VEntityField } from "@halo-dev/components";
|
||||
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"post:list-item:field:create": (post: Ref<ListedPost>) => {
|
||||
return [
|
||||
{
|
||||
priority: 0,
|
||||
position: "end",
|
||||
component: markRaw(VEntityField),
|
||||
props: {
|
||||
description: post.value.post.spec.slug,
|
||||
},
|
||||
permissions: [],
|
||||
hidden: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### ListedPost
|
||||
|
||||
```mdx-code-block
|
||||
import ListedPost from "./interface/ListedPost.md";
|
||||
|
||||
<ListedPost />
|
||||
```
|
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: 文章数据列表操作菜单
|
||||
description: 扩展文章数据列表操作菜单 - post:list-item:operation:create
|
||||
---
|
||||
|
||||
此扩展点用于扩展文章数据列表的操作菜单项。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"post:list-item:operation:create": (
|
||||
post: Ref<ListedPost>
|
||||
): OperationItem<ListedPost>[] | Promise<OperationItem<ListedPost>[]> => {
|
||||
return [
|
||||
{
|
||||
priority: 10,
|
||||
component: markRaw(VDropdownItem),
|
||||
props: {},
|
||||
action: (item?: ListedPost) => {
|
||||
// do something
|
||||
},
|
||||
label: "foo",
|
||||
hidden: false,
|
||||
permissions: [],
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```mdx-code-block
|
||||
import OperationItem from "./interface/OperationItem.md";
|
||||
|
||||
<OperationItem />
|
||||
```
|
||||
|
||||
## 示例
|
||||
|
||||
此示例将实现一个操作菜单项,点击后会将文章内容作为文件下载到本地。
|
||||
|
||||
```ts
|
||||
import type { ListedPost } from "@halo-dev/api-client";
|
||||
import { VDropdownItem } from "@halo-dev/components";
|
||||
import { definePlugin } from "@halo-dev/console-shared";
|
||||
import axios from "axios";
|
||||
import { markRaw } from "vue";
|
||||
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"post:list-item:operation:create": () => {
|
||||
return [
|
||||
{
|
||||
priority: 21,
|
||||
component: markRaw(VDropdownItem),
|
||||
label: "下载到本地",
|
||||
visible: true,
|
||||
permissions: [],
|
||||
action: async (post: ListedPost) => {
|
||||
const { data } = await axios.get(
|
||||
`/apis/api.console.halo.run/v1alpha1/posts/${post.post.metadata.name}/head-content`
|
||||
);
|
||||
const blob = new Blob([data.raw], {
|
||||
type: "text/plain;charset=utf-8",
|
||||
});
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = `${post.post.spec.title}.${data.rawType}`;
|
||||
link.click();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### ListedPost
|
||||
|
||||
```mdx-code-block
|
||||
import ListedPost from "./interface/ListedPost.md";
|
||||
|
||||
<ListedPost />
|
||||
```
|
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: 回复数据列表操作菜单
|
||||
description: 扩展回复数据列表操作菜单 - reply:list-item:operation:create
|
||||
---
|
||||
|
||||
此扩展点用于扩展回复数据列表的操作菜单项。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"reply:list-item:operation:create": (
|
||||
reply: Ref<ListedReply>
|
||||
): OperationItem<ListedReply>[] | Promise<OperationItem<ListedReply>[]> => {
|
||||
return [
|
||||
{
|
||||
priority: 10,
|
||||
component: markRaw(VDropdownItem),
|
||||
props: {},
|
||||
action: (item?: ListedReply) => {
|
||||
// do something
|
||||
},
|
||||
label: "foo",
|
||||
hidden: false,
|
||||
permissions: [],
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```mdx-code-block
|
||||
import OperationItem from "./interface/OperationItem.md";
|
||||
|
||||
<OperationItem />
|
||||
```
|
||||
|
||||
## 示例
|
||||
|
||||
此示例将实现一个操作菜单项。
|
||||
|
||||
```ts
|
||||
import type { ListedReply } from "@halo-dev/api-client";
|
||||
import { VDropdownItem } from "@halo-dev/components";
|
||||
import { definePlugin } from "@halo-dev/console-shared";
|
||||
import { markRaw } from "vue";
|
||||
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"reply:list-item:operation:create": () => {
|
||||
return [
|
||||
{
|
||||
priority: 21,
|
||||
component: markRaw(VDropdownItem),
|
||||
label: "测试回复菜单",
|
||||
visible: true,
|
||||
permissions: [],
|
||||
action: async (reply: ListedReply) => {
|
||||
console.log(reply)
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
### ListedReply
|
||||
|
||||
```mdx-code-block
|
||||
import ListedReply from "./interface/ListedReply.md";
|
||||
|
||||
<ListedReply />
|
||||
```
|
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: 主题数据列表操作菜单
|
||||
description: 扩展主题数据列表操作菜单 - theme:list-item:operation:create
|
||||
---
|
||||
|
||||
此扩展点用于扩展主题数据列表的操作菜单项。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"theme:list-item:operation:create": (
|
||||
theme: Ref<Theme>
|
||||
): OperationItem<Theme>[] | Promise<OperationItem<Theme>[]> => {
|
||||
return [
|
||||
{
|
||||
priority: 10,
|
||||
component: markRaw(VDropdownItem),
|
||||
props: {},
|
||||
action: (item?: Theme) => {
|
||||
// do something
|
||||
},
|
||||
label: "foo",
|
||||
hidden: false,
|
||||
permissions: [],
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```mdx-code-block
|
||||
import OperationItem from "./interface/OperationItem.md";
|
||||
|
||||
<OperationItem />
|
||||
```
|
||||
|
||||
## 示例
|
||||
|
||||
此示例将实现一个跳转到前台预览主题的操作菜单项。
|
||||
|
||||
```ts
|
||||
import { definePlugin, type OperationItem } from "@halo-dev/console-shared";
|
||||
import { VButton } from "@halo-dev/components";
|
||||
import { markRaw, type Ref } from "vue";
|
||||
import type { Theme } from "@halo-dev/api-client";
|
||||
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"theme:list-item:operation:create": (
|
||||
theme: Ref<Theme>
|
||||
): OperationItem<Theme>[] | Promise<OperationItem<Theme>[]> => {
|
||||
return [
|
||||
{
|
||||
priority: 10,
|
||||
component: markRaw(VButton),
|
||||
props: {
|
||||
size: "sm",
|
||||
},
|
||||
action: (item?: Theme) => {
|
||||
window.open(`/?preview-theme=${item?.metadata.name}`);
|
||||
},
|
||||
label: "前台预览",
|
||||
hidden: false,
|
||||
permissions: [],
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 实现案例
|
||||
|
||||
- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store)
|
||||
|
||||
## 类型定义
|
||||
|
||||
### Theme
|
||||
|
||||
```mdx-code-block
|
||||
import Theme from "./interface/Theme.md";
|
||||
|
||||
<Theme />
|
||||
```
|
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: 主题管理界面选项卡
|
||||
description: 扩展主题管理界面选项卡 - theme:list:tabs:create
|
||||
---
|
||||
|
||||
目前在 Halo 的主题管理中原生支持本地上传和远程下载的方式安装主题,此扩展点用于扩展主题管理界面的选项卡,以支持更多的安装方式。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"theme:list:tabs:create": (): ThemeListTab[] | Promise<ThemeListTab[]> => {
|
||||
return [
|
||||
{
|
||||
id: "foo",
|
||||
label: "foo",
|
||||
component: markRaw(FooComponent),
|
||||
props: {},
|
||||
permissions: [],
|
||||
priority: 0,
|
||||
}
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts title="ThemeListTab"
|
||||
export interface ThemeListTab {
|
||||
id: string; // 选项卡 ID
|
||||
label: string; // 选项卡标题
|
||||
component: Raw<Component>; // 选项卡面板组件
|
||||
props?: Record\<string, unknown\>; // 选项卡面板组件属性
|
||||
permissions?: string[]; // 选项卡 UI 权限
|
||||
priority: number; // 选项卡排序优先级
|
||||
}
|
||||
```
|
||||
|
||||
## 实现案例
|
||||
|
||||
- [https://github.com/halo-dev/plugin-app-store](https://github.com/halo-dev/plugin-app-store)
|
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: 个人资料选项卡
|
||||
description: 扩展个人中心的个人资料选项卡 - uc:user:profile:tabs:create
|
||||
---
|
||||
|
||||
此扩展点用于扩展个人中心的个人资料选项卡。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"uc:user:profile:tabs:create": (): UserProfileTab[] | Promise<UserProfileTab[]> => {
|
||||
return [
|
||||
{
|
||||
id: "foo",
|
||||
label: "foo",
|
||||
component: markRaw(FooComponent),
|
||||
priority: 20,
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts title="UserProfileTab"
|
||||
export interface UserProfileTab {
|
||||
id: string; // 选项卡 ID
|
||||
label: string; // 选项卡标题
|
||||
component: Raw<Component>; // 选项卡面板组件
|
||||
priority: number; // 排序优先级
|
||||
}
|
||||
```
|
||||
|
||||
其中,`component` 组件有以下实现要求:
|
||||
|
||||
1. 组件包含以下 props:
|
||||
1. `user:DetailedUser`:当前用户信息。
|
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: 用户详情选项卡
|
||||
description: 扩展用户详情选项卡 - user:detail:tabs:create
|
||||
---
|
||||
|
||||
此扩展点用于扩展用户详情页面的选项卡。
|
||||
|
||||

|
||||
|
||||
## 定义方式
|
||||
|
||||
```ts
|
||||
export default definePlugin({
|
||||
extensionPoints: {
|
||||
"user:detail:tabs:create": (): UserTab[] | Promise<UserTab[]> => {
|
||||
return [
|
||||
{
|
||||
id: "foo",
|
||||
label: "foo",
|
||||
component: markRaw(FooComponent),
|
||||
priority: 20,
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts title="UserTab"
|
||||
export interface UserTab {
|
||||
id: string; // 选项卡 ID
|
||||
label: string; // 选项卡标题
|
||||
component: Raw<Component>; // 选项卡面板组件
|
||||
priority: number; // 排序优先级
|
||||
}
|
||||
```
|
||||
|
||||
其中,`component` 组件有以下实现要求:
|
||||
|
||||
1. 组件包含以下 props:
|
||||
1. `user:DetailedUser`:当前用户信息。
|
@@ -0,0 +1,120 @@
|
||||
---
|
||||
title: 路由定义
|
||||
description: 通过插件为 Console 控制台和 UC 个人中心添加新路由
|
||||
---
|
||||
|
||||
Halo 为插件提供了为 Console 控制台和 UC 个人中心添加新路由的入口,可以用于为插件单独提供一个页面。
|
||||
|
||||
此文档将介绍如何定义路由以及侧边菜单项。
|
||||
|
||||
## 定义方式
|
||||
|
||||
Console 控制台和 UC 个人中心的路由定义基本和 Vue Router 官方的保持一致,为了区分 Console 控制台和 UC 个人中心的路由,Halo 为插件提供了两个不同的路由定义入口。
|
||||
|
||||
- `routes`:Console 控制台路由定义
|
||||
- `ucRoutes`:UC 个人中心路由定义
|
||||
|
||||
```ts
|
||||
import HomeView from "./views/HomeView.vue"
|
||||
import { IconComputer } from "@halo-dev/components";
|
||||
|
||||
export default definePlugin({
|
||||
routes: [ // Console 控制台路由定义
|
||||
{
|
||||
parentName: "Root",
|
||||
route: {
|
||||
path: "/foo",
|
||||
name: "Foo",
|
||||
component: HomeView,
|
||||
meta: {
|
||||
permissions: [""],
|
||||
menu: {
|
||||
name: "Foo",
|
||||
group: "content",
|
||||
icon: markRaw(IconComputer),
|
||||
priority: 40
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
ucRoutes: [ // UC 个人中心路由定义
|
||||
{
|
||||
parentName: "Root",
|
||||
route: {
|
||||
path: "/uc-foo",
|
||||
name: "FooUC",
|
||||
component: HomeView,
|
||||
meta: {
|
||||
permissions: [""],
|
||||
menu: {
|
||||
name: "FooUC",
|
||||
group: "content",
|
||||
icon: markRaw(IconComputer),
|
||||
priority: 40
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
## 类型定义
|
||||
|
||||
```ts
|
||||
{
|
||||
routes?: RouteRecordRaw[] | RouteRecordAppend[];
|
||||
ucRoutes?: RouteRecordRaw[] | RouteRecordAppend[];
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
export interface RouteRecordAppend {
|
||||
parentName: RouteRecordName;
|
||||
route: RouteRecordRaw;
|
||||
}
|
||||
```
|
||||
|
||||
- `parentName`:父路由名称,主要用于确认页面 Layout,如果想要添加到顶级路由,可以设置为 `Root`。如果不需要设置父路由,可以完全使用 `RouteRecordRaw` 定义。此外,如果同时设置了 `parentName` 以及其下路由设置了 `meta.menu`,那么此路由的菜单项将成为父菜单的子菜单项,可支持的父路由名称如下:
|
||||
- Console:
|
||||
- `AttachmentsRoot`(附件)
|
||||
- `CommentsRoot`(评论)
|
||||
- `SinglePagesRoot`(页面)
|
||||
- `PostsRoot`(文章)
|
||||
- `MenusRoot`(菜单)
|
||||
- `ThemeRoot`(主题)
|
||||
- `OverviewRoot`(概览)
|
||||
- `BackupRoot`(备份)
|
||||
- `PluginsRoot`(插件)
|
||||
- `SettingsRoot`(设置)
|
||||
- `UsersRoot`(用户)
|
||||
- `ToolsRoot`(工具)
|
||||
- UC:
|
||||
- `PostsRoot`(文章)
|
||||
- `NotificationsRoot`(消息)
|
||||
|
||||
:::info 提示
|
||||
`RouteRecordRaw` 来自 Vue Router,详见 [API 文档 | Vue Router](https://router.vuejs.org/zh/api/#Type-Aliases-RouteRecordRaw)
|
||||
:::
|
||||
|
||||
此外,为了方便插件在 Console 控制台和 UC 个人中心添加菜单项等操作,Halo 为 `RouteRecordRaw` 添加了 `meta` 属性,该属性为 `RouteMeta` 类型,定义如下:
|
||||
|
||||
```ts
|
||||
interface RouteMeta {
|
||||
title?: string; // 浏览器标题
|
||||
searchable?: boolean; // 是否可以在 Console 的全局搜索中搜索到
|
||||
permissions?: string[]; // UI 权限
|
||||
menu?: { // 侧边菜单配置
|
||||
name: string; // 菜单名称
|
||||
group?: CoreMenuGroupId; // 内置菜单分组 ID,如果不使用内置的分组,也可以直接填写分组名称
|
||||
icon?: Component; // 菜单图标,类型为 Vue 组件,推荐使用 https://github.com/unplugin/unplugin-icons
|
||||
priority: number; // 菜单项排序,数字越小越靠前
|
||||
mobile?: boolean; // 是否在移动端显示
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
export type CoreMenuGroupId = "dashboard" | "content" | "interface" | "system" | "tool";
|
||||
```
|
Reference in New Issue
Block a user