edbf78f236
- Halo Plugin 后端(Java/Gradle),含 DarkModePlugin 主类和测试 - Vue 3 + TypeScript 前端 UI,包含主题切换组件和设置页面 - 暗色模式 CSS 变量和覆盖样式(布局/编辑器/表单/滚动条等) - 设计文档和调查文档 - Halo 插件/主题开发 Agent Skills Co-Authored-By: Claude <noreply@anthropic.com>
253 lines
11 KiB
Markdown
253 lines
11 KiB
Markdown
# UI Components
|
|
|
|
Halo provides two layers of UI primitives for plugin frontends:
|
|
|
|
1. **Base component library** (`@halo-dev/components`) — install and import explicitly
|
|
2. **Business components & directives** — globally registered, use directly without import
|
|
|
|
> Full base component docs: https://halo-ui-components.halo-run.workers.dev
|
|
>
|
|
> For forms, see [ui-forms.md](ui-forms.md) — Halo uses FormKit (globally registered) with many custom inputs.
|
|
|
|
## Official Docs Routing
|
|
|
|
Component APIs change across Halo versions. Treat the lists below as common
|
|
shortcuts, not an exhaustive API reference.
|
|
|
|
| Need | Official docs |
|
|
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| Base components from `@halo-dev/components` | https://halo-ui-components.halo-run.workers.dev |
|
|
| Business component index | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/ui/components/index.md |
|
|
| `AttachmentSelectorModal` | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/ui/components/attachment-selector-modal.md |
|
|
| `AttachmentFileTypeIcon` | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/ui/components/attachment-file-type-icon.md |
|
|
| `AnnotationsForm` | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/ui/components/annotations-form.md |
|
|
| `FilterDropdown` / `FilterCleanButton` | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/ui/components/filter-dropdown.md |
|
|
| `HasPermission` | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/ui/components/has-permission.md |
|
|
| `PluginDetailModal` | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/ui/components/plugin-detail-modal.md |
|
|
| `SearchInput` | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/ui/components/search-input.md |
|
|
| `UppyUpload` | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/ui/components/uppy-upload.md |
|
|
| `VCodemirror` | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/ui/components/v-codemirror.md |
|
|
| `v-permission` | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/ui/components/v-permission.md |
|
|
| `v-tooltip` | https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/plugin/api-reference/ui/components/v-tooltip.md |
|
|
|
|
## Base Component Library
|
|
|
|
Install:
|
|
|
|
```bash
|
|
pnpm install @halo-dev/components
|
|
```
|
|
|
|
Import and use:
|
|
|
|
```vue
|
|
<script lang="ts" setup>
|
|
import { ref } from "vue";
|
|
import { VButton, VModal, VCard } from "@halo-dev/components";
|
|
const visible = ref(false);
|
|
</script>
|
|
|
|
<template>
|
|
<VButton type="secondary" @click="visible = true">Open</VButton>
|
|
<VModal v-if="visible" @close="visible = false" title="Title">
|
|
<VCard>Content</VCard>
|
|
</VModal>
|
|
</template>
|
|
```
|
|
|
|
Common exported components from `@halo-dev/components`:
|
|
|
|
| Component | Purpose |
|
|
| ----------------------------------------------- | -------------------------------------------------------------------------- |
|
|
| `VAlert` | Alert banner |
|
|
| `VAvatar` / `VAvatarGroup` | User avatar(s) |
|
|
| `VButton` | Button with variants: `default`, `primary`, `secondary`, `danger`, `ghost` |
|
|
| `VCard` | Card container |
|
|
| `VDescription` / `VDescriptionItem` | Key-value description list |
|
|
| `VDialog` + `Dialog` (manager) | Dialog with imperative API |
|
|
| `VDropdownDivider` / `VDropdownItem` | Dropdown menu items |
|
|
| `VEmpty` | Empty state placeholder |
|
|
| `VEntity` / `VEntityContainer` / `VEntityField` | Entity list item layout |
|
|
| `VLoading` | Loading spinner/overlay |
|
|
| `VMenu` / `VMenuItem` / `VMenuLabel` | Menu navigation |
|
|
| `VModal` | Modal with `v-model:visible` |
|
|
| `VPagination` | Pagination control |
|
|
| `VPageHeader` | Page header with back button/title/actions |
|
|
| `VSpace` | Flex spacing layout |
|
|
| `VStatusDot` | Status indicator dot |
|
|
| `VSwitch` | Toggle switch |
|
|
| `VTabbar` / `VTabs` / `VTabItem` | Tab navigation |
|
|
| `VTag` | Colored tag/badge |
|
|
| `Toast` (manager) | Toast notification imperative API |
|
|
| `VTooltipComponent` / `vTooltip` | Tooltip component/directive |
|
|
|
|
## Business Components (Globally Registered)
|
|
|
|
These are available without import in any plugin Vue component.
|
|
|
|
### VCodemirror
|
|
|
|
Code editor.
|
|
|
|
```vue
|
|
<VCodemirror v-model="value" height="300px" language="yaml" />
|
|
```
|
|
|
|
| Prop | Type | Default | Description |
|
|
| ------------ | -------- | -------- | --------------------------------------------- |
|
|
| `modelValue` | `string` | `""` | Binding value |
|
|
| `height` | `string` | `"auto"` | Editor height |
|
|
| `language` | `string` | `"yaml"` | Language: `yaml`, `html`, `js`, `css`, `json` |
|
|
| `extensions` | `array` | `[]` | Codemirror extensions |
|
|
|
|
### AttachmentSelectorModal
|
|
|
|
Attachment picker modal (Console only).
|
|
|
|
```vue
|
|
<script setup>
|
|
const visible = ref(false);
|
|
function onSelect(attachments) {
|
|
console.log(attachments); // AttachmentLike[]
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<VButton @click="visible = true">Select</VButton>
|
|
<AttachmentSelectorModal
|
|
v-if="visible"
|
|
@close="visible = false"
|
|
:accepts="['image/*']"
|
|
:min="1"
|
|
:max="5"
|
|
@select="onSelect"
|
|
/>
|
|
</template>
|
|
```
|
|
|
|
| Prop | Type | Default | Description |
|
|
| ------------- | ---------- | --------- | ----------------------- |
|
|
| `visible` | `boolean` | `false` | Controlled visibility |
|
|
| `accepts` | `string[]` | `["*/*"]` | Accepted MIME types |
|
|
| `min` / `max` | `number` | — | Min/max selection count |
|
|
|
|
### UppyUpload
|
|
|
|
File upload component.
|
|
|
|
```vue
|
|
<UppyUpload
|
|
endpoint="/apis/api.console.halo.run/v1alpha1/attachments/upload"
|
|
:meta="{ policyName, groupName }"
|
|
@uploaded="onUploaded"
|
|
@error="onError"
|
|
/>
|
|
```
|
|
|
|
| Prop | Type | Default | Description |
|
|
| ------------- | ------------------------- | -------- | ------------------------------- |
|
|
| `endpoint` | `string` | required | Upload API endpoint |
|
|
| `meta` | `Record<string, unknown>` | — | Extra metadata sent with upload |
|
|
| `autoProceed` | `boolean` | `false` | Auto-upload on select |
|
|
| `method` | `string` | `"post"` | HTTP method |
|
|
|
|
### SearchInput
|
|
|
|
Search input that only triggers on Enter (not while typing).
|
|
|
|
```vue
|
|
<SearchInput v-model="keyword" placeholder="Search..." />
|
|
```
|
|
|
|
### AttachmentFileTypeIcon
|
|
|
|
File-type icon for attachment/file lists.
|
|
|
|
```vue
|
|
<AttachmentFileTypeIcon fileName="example.png" :display-ext="true" />
|
|
```
|
|
|
|
### AnnotationsForm
|
|
|
|
Renders the Annotations form for a given Extension group/kind.
|
|
|
|
```vue
|
|
<script setup>
|
|
const formRef = ref();
|
|
|
|
async function handleSubmit() {
|
|
formRef.value?.handleSubmit();
|
|
await nextTick();
|
|
const { customAnnotations, annotations, customFormInvalid, specFormInvalid } =
|
|
formRef.value || {};
|
|
if (customFormInvalid || specFormInvalid) return;
|
|
const merged = { ...annotations, ...customAnnotations };
|
|
// ...submit merged
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<AnnotationsForm ref="formRef" :value="currentAnnotations" kind="Post" group="content.halo.run" />
|
|
<VButton @click="handleSubmit">Save</VButton>
|
|
</template>
|
|
```
|
|
|
|
### FilterDropdown / FilterCleanButton
|
|
|
|
Generic filter dropdown and clear button for list pages.
|
|
|
|
```vue
|
|
<FilterDropdown
|
|
v-model="sortValue"
|
|
label="Sort"
|
|
:items="[
|
|
{ label: 'Newest', value: 'creationTimestamp,desc' },
|
|
{ label: 'Oldest', value: 'creationTimestamp,asc' },
|
|
]"
|
|
/>
|
|
|
|
<FilterCleanButton @click="resetFilters" />
|
|
```
|
|
|
|
### PluginDetailModal
|
|
|
|
Open a plugin's detail/settings modal inline.
|
|
|
|
```vue
|
|
<PluginDetailModal v-if="visible" name="my-plugin" @close="visible = false" />
|
|
```
|
|
|
|
| Prop | Type | Description |
|
|
| ------ | -------- | -------------------- |
|
|
| `name` | `string` | Plugin metadata.name |
|
|
|
|
### HasPermission
|
|
|
|
Render content only when the user has the required permissions.
|
|
|
|
```vue
|
|
<HasPermission :permissions="['system:posts:manage']">
|
|
<VButton type="danger">Delete</VButton>
|
|
</HasPermission>
|
|
```
|
|
|
|
## Directives (Globally Registered)
|
|
|
|
### v-permission
|
|
|
|
Conditionally render based on permissions.
|
|
|
|
```vue
|
|
<VButton type="danger" v-permission="['system:posts:manage']">Delete</VButton>
|
|
```
|
|
|
|
Equivalent component: `<HasPermission :permissions="['system:posts:manage']">...</HasPermission>`
|
|
|
|
### v-tooltip
|
|
|
|
Add tooltip to any element.
|
|
|
|
```vue
|
|
<IconDeleteBin v-tooltip="'Delete this item'" />
|
|
```
|