mirror of https://github.com/halo-dev/halo
feat: add formkit custom input of attachment (halo-dev/console#674)
#### What type of PR is this? /kind feature /milestone 2.0 #### What this PR does / why we need it: 添加选择附件类型的 FormKit 输入框。 在 Vue 单组件中使用: ```vue <script lang="ts" setup> const logo = ref("") </script> <template> <FormKit v-model="logo" label="Logo" type="attachment" validation="required" /> </template> ``` 在 FormKit Schema 中使用(插件 / 主题设置表单定义): ```yaml - $formkit: attachment name: logo label: Logo ``` #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/2558 #### Screenshots: <img width="671" alt="image" src="https://user-images.githubusercontent.com/21301288/198980581-ba90ec32-f205-4d03-8546-3c93238298e7.png"> #### Special notes for your reviewer: /cc @halo-dev/sig-halo-console #### Does this PR introduce a user-facing change? ```release-note 添加选择附件类型的 FormKit 输入框。 ```pull/3445/head
parent
f77be1ecb7
commit
9a24604a6b
|
@ -12,6 +12,7 @@
|
||||||
- 参数
|
- 参数
|
||||||
1. language: 目前支持 `yaml`, `html`, `css`, `javascript`, `json`
|
1. language: 目前支持 `yaml`, `html`, `css`, `javascript`, `json`
|
||||||
2. height: 编辑器高度,如:`100px`
|
2. height: 编辑器高度,如:`100px`
|
||||||
|
- `attachment`: 附件选择
|
||||||
- `menuCheckbox`:选择一组菜单
|
- `menuCheckbox`:选择一组菜单
|
||||||
- `menuRadio`:选择一个菜单
|
- `menuRadio`:选择一个菜单
|
||||||
- `menuItemSelect`:选择菜单项
|
- `menuItemSelect`:选择菜单项
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { createAutoAnimatePlugin } from "@formkit/addons";
|
||||||
import { zh } from "@formkit/i18n";
|
import { zh } from "@formkit/i18n";
|
||||||
import type { DefaultConfigOptions } from "@formkit/vue";
|
import type { DefaultConfigOptions } from "@formkit/vue";
|
||||||
import { form } from "./inputs/form";
|
import { form } from "./inputs/form";
|
||||||
|
import { attachment } from "./inputs/attachment";
|
||||||
import { code } from "./inputs/code";
|
import { code } from "./inputs/code";
|
||||||
import { menuCheckbox } from "./inputs/menu-checkbox";
|
import { menuCheckbox } from "./inputs/menu-checkbox";
|
||||||
import { menuRadio } from "./inputs/menu-radio";
|
import { menuRadio } from "./inputs/menu-radio";
|
||||||
|
@ -22,6 +23,7 @@ const config: DefaultConfigOptions = {
|
||||||
plugins: [createAutoAnimatePlugin()],
|
plugins: [createAutoAnimatePlugin()],
|
||||||
inputs: {
|
inputs: {
|
||||||
form,
|
form,
|
||||||
|
attachment,
|
||||||
code,
|
code,
|
||||||
menuCheckbox,
|
menuCheckbox,
|
||||||
menuRadio,
|
menuRadio,
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { FormKitFrameworkContext } from "@formkit/core";
|
||||||
|
import { IconFolder } from "@halo-dev/components";
|
||||||
|
import AttachmentSelectorModal from "@/modules/contents/attachments/components/AttachmentSelectorModal.vue";
|
||||||
|
import { ref, type PropType } from "vue";
|
||||||
|
import type { AttachmentLike } from "@halo-dev/console-shared";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
context: {
|
||||||
|
type: Object as PropType<FormKitFrameworkContext>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const attachmentSelectorModal = ref(false);
|
||||||
|
|
||||||
|
const onInput = (e: Event) => {
|
||||||
|
props.context.handlers.DOMInput(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAttachmentSelect = (attachments: AttachmentLike[]) => {
|
||||||
|
const urls: (string | undefined)[] = attachments.map((attachment) => {
|
||||||
|
if (typeof attachment === "string") {
|
||||||
|
return attachment;
|
||||||
|
}
|
||||||
|
if ("url" in attachment) {
|
||||||
|
return attachment.url;
|
||||||
|
}
|
||||||
|
if ("spec" in attachment) {
|
||||||
|
return attachment.status?.permalink;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (urls.length) {
|
||||||
|
props.context.node.input(urls[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<input
|
||||||
|
:id="context.id"
|
||||||
|
:value="context._value"
|
||||||
|
:class="context.classes.input"
|
||||||
|
:name="context.node.name"
|
||||||
|
v-bind="context.attrs"
|
||||||
|
type="text"
|
||||||
|
@blur="context.handlers.blur()"
|
||||||
|
@input="onInput"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="group flex h-full cursor-pointer items-center border-l px-3 transition-all hover:bg-gray-100"
|
||||||
|
@click="attachmentSelectorModal = true"
|
||||||
|
>
|
||||||
|
<IconFolder class="h-4 w-4 text-gray-500 group-hover:text-gray-700" />
|
||||||
|
</div>
|
||||||
|
<AttachmentSelectorModal
|
||||||
|
v-model:visible="attachmentSelectorModal"
|
||||||
|
@select="onAttachmentSelect"
|
||||||
|
/>
|
||||||
|
</template>
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { initialValue } from "@formkit/inputs";
|
||||||
|
import { createInput } from "@formkit/vue";
|
||||||
|
import AttachmentInput from "./AttachmentInput.vue";
|
||||||
|
|
||||||
|
export const attachment = createInput(AttachmentInput, {
|
||||||
|
type: "input",
|
||||||
|
props: [],
|
||||||
|
forceTypeProp: "text",
|
||||||
|
features: [initialValue],
|
||||||
|
});
|
|
@ -98,6 +98,7 @@ onMounted(async () => {
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<AttachmentGroupEditingModal
|
<AttachmentGroupEditingModal
|
||||||
|
v-if="!readonly"
|
||||||
v-model:visible="editingModal"
|
v-model:visible="editingModal"
|
||||||
:group="groupToUpdate"
|
:group="groupToUpdate"
|
||||||
@close="onEditingModalClose"
|
@close="onEditingModalClose"
|
||||||
|
|
|
@ -61,6 +61,7 @@ const handleConfirm = () => {
|
||||||
<VModal
|
<VModal
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
:width="1240"
|
:width="1240"
|
||||||
|
:mount-to-body="true"
|
||||||
title="选择附件"
|
title="选择附件"
|
||||||
height="calc(100vh - 20px)"
|
height="calc(100vh - 20px)"
|
||||||
@update:visible="onVisibleChange"
|
@update:visible="onVisibleChange"
|
||||||
|
|
|
@ -272,7 +272,7 @@ watchEffect(() => {
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.page.spec.cover"
|
v-model="formState.page.spec.cover"
|
||||||
label="封面图"
|
label="封面图"
|
||||||
type="text"
|
type="attachment"
|
||||||
name="cover"
|
name="cover"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
</FormKit>
|
</FormKit>
|
||||||
|
|
|
@ -152,7 +152,8 @@ watch(
|
||||||
help="需要主题适配以支持"
|
help="需要主题适配以支持"
|
||||||
name="cover"
|
name="cover"
|
||||||
label="封面图"
|
label="封面图"
|
||||||
type="text"
|
type="attachment"
|
||||||
|
validation="required"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.spec.description"
|
v-model="formState.spec.description"
|
||||||
|
|
|
@ -285,7 +285,7 @@ watchEffect(() => {
|
||||||
v-model="formState.post.spec.cover"
|
v-model="formState.post.spec.cover"
|
||||||
name="cover"
|
name="cover"
|
||||||
label="封面图"
|
label="封面图"
|
||||||
type="text"
|
type="attachment"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
</FormKit>
|
</FormKit>
|
||||||
<!--TODO: add SEO/Metas/Inject Code form-->
|
<!--TODO: add SEO/Metas/Inject Code form-->
|
||||||
|
|
|
@ -171,7 +171,7 @@ watch(
|
||||||
name="cover"
|
name="cover"
|
||||||
help="需要主题适配以支持"
|
help="需要主题适配以支持"
|
||||||
label="封面图"
|
label="封面图"
|
||||||
type="text"
|
type="attachment"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
</FormKit>
|
</FormKit>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|
|
@ -197,7 +197,7 @@ const handleRawModeChange = () => {
|
||||||
<FormKit
|
<FormKit
|
||||||
v-model="formState.spec.avatar"
|
v-model="formState.spec.avatar"
|
||||||
label="头像"
|
label="头像"
|
||||||
type="text"
|
type="attachment"
|
||||||
name="avatar"
|
name="avatar"
|
||||||
validation="url"
|
validation="url"
|
||||||
></FormKit>
|
></FormKit>
|
||||||
|
|
Loading…
Reference in New Issue