fix: form item overflow fixed and layout improved (#5572)

* fix: form item overflow fixed and layout improved

* fix: basic form demo update

* feat: form label support render

* fix: form docs update
This commit is contained in:
Netfan 2025-02-20 23:05:08 +08:00 committed by GitHub
parent ccd99eb24d
commit a221d2b491
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 80 additions and 56 deletions

View File

@ -445,9 +445,9 @@ export interface FormSchema<
/** 字段名,也作为自定义插槽的名称 */ /** 字段名,也作为自定义插槽的名称 */
fieldName: string; fieldName: string;
/** 帮助信息 */ /** 帮助信息 */
help?: string; help?: CustomRenderType;
/** 表单 */ /** 表单的标签如果是一个string会用于默认必选规则的消息提示 */
label?: string; label?: CustomRenderType;
/** 自定义组件内部渲染 */ /** 自定义组件内部渲染 */
renderComponentContent?: RenderComponentContentType; renderComponentContent?: RenderComponentContentType;
/** 字段规则 */ /** 字段规则 */

View File

@ -538,4 +538,6 @@ interface Preferences {
- `overridesPreferences`方法只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置。 - `overridesPreferences`方法只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置。
- 任何配置项都可以覆盖,只需要在`overridesPreferences`方法内覆盖即可,不要修改默认配置文件。 - 任何配置项都可以覆盖,只需要在`overridesPreferences`方法内覆盖即可,不要修改默认配置文件。
- 更改配置后请清空缓存,否则可能不生效。::: - 更改配置后请清空缓存,否则可能不生效。
:::

View File

@ -193,7 +193,7 @@ const fieldProps = computed(() => {
const rules = fieldRules.value; const rules = fieldRules.value;
return { return {
keepValue: true, keepValue: true,
label, label: isString(label) ? label : '',
...(rules ? { rules } : {}), ...(rules ? { rules } : {}),
...(formFieldProps as Record<string, any>), ...(formFieldProps as Record<string, any>),
}; };
@ -285,7 +285,7 @@ function autofocus() {
'pb-6': !compact, 'pb-6': !compact,
'pb-2': compact, 'pb-2': compact,
}" }"
class="flex" class="relative flex"
v-bind="$attrs" v-bind="$attrs"
> >
<FormLabel <FormLabel
@ -305,55 +305,59 @@ function autofocus() {
:style="labelStyle" :style="labelStyle"
> >
<template v-if="label"> <template v-if="label">
<span>{{ label }}</span> <VbenRenderContent :content="label" />
<span v-if="colon" class="ml-[2px]">:</span> <span v-if="colon" class="ml-[2px]">:</span>
</template> </template>
</FormLabel> </FormLabel>
<div :class="cn('relative flex w-full items-center', wrapperClass)"> <div class="w-full overflow-hidden">
<FormControl :class="cn(controlClass)"> <div :class="cn('relative flex w-full items-center', wrapperClass)">
<slot <div class="flex-auto overflow-hidden">
v-bind="{ <FormControl :class="cn(controlClass)">
...slotProps, <slot
...createComponentProps(slotProps), v-bind="{
disabled: shouldDisabled, ...slotProps,
isInValid, ...createComponentProps(slotProps),
}" disabled: shouldDisabled,
>
<component
:is="FieldComponent"
ref="fieldComponentRef"
:class="{
'border-destructive focus:border-destructive hover:border-destructive/80 focus:shadow-[0_0_0_2px_rgba(255,38,5,0.06)]':
isInValid, isInValid,
}" }"
v-bind="createComponentProps(slotProps)"
:disabled="shouldDisabled"
>
<template
v-for="name in renderContentKey"
:key="name"
#[name]="renderSlotProps"
> >
<VbenRenderContent <component
:content="customContentRender[name]" :is="FieldComponent"
v-bind="{ ...renderSlotProps, formContext: slotProps }" ref="fieldComponentRef"
/> :class="{
</template> 'border-destructive focus:border-destructive hover:border-destructive/80 focus:shadow-[0_0_0_2px_rgba(255,38,5,0.06)]':
<!-- <slot></slot> --> isInValid,
</component> }"
</slot> v-bind="createComponentProps(slotProps)"
</FormControl> :disabled="shouldDisabled"
<!-- 自定义后缀 --> >
<div v-if="suffix" class="ml-1"> <template
<VbenRenderContent :content="suffix" /> v-for="name in renderContentKey"
:key="name"
#[name]="renderSlotProps"
>
<VbenRenderContent
:content="customContentRender[name]"
v-bind="{ ...renderSlotProps, formContext: slotProps }"
/>
</template>
<!-- <slot></slot> -->
</component>
</slot>
</FormControl>
</div>
<!-- 自定义后缀 -->
<div v-if="suffix" class="ml-1">
<VbenRenderContent :content="suffix" />
</div>
<FormDescription v-if="description" class="ml-1">
<VbenRenderContent :content="description" />
</FormDescription>
</div> </div>
<FormDescription v-if="description">
<VbenRenderContent :content="description" />
</FormDescription>
<Transition name="slide-up"> <Transition name="slide-up">
<FormMessage class="absolute -bottom-[22px]" /> <FormMessage class="absolute bottom-1" />
</Transition> </Transition>
</div> </div>
</FormItem> </FormItem>

View File

@ -1,10 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { FormLabel, VbenHelpTooltip } from '@vben-core/shadcn-ui'; import type { CustomRenderType } from '../types';
import {
FormLabel,
VbenHelpTooltip,
VbenRenderContent,
} from '@vben-core/shadcn-ui';
import { cn } from '@vben-core/shared/utils'; import { cn } from '@vben-core/shared/utils';
interface Props { interface Props {
class?: string; class?: string;
help?: string; help?: CustomRenderType;
required?: boolean; required?: boolean;
} }
@ -16,7 +22,7 @@ const props = defineProps<Props>();
<span v-if="required" class="text-destructive mr-[2px]">*</span> <span v-if="required" class="text-destructive mr-[2px]">*</span>
<slot></slot> <slot></slot>
<VbenHelpTooltip v-if="help" trigger-class="size-3.5 ml-1"> <VbenHelpTooltip v-if="help" trigger-class="size-3.5 ml-1">
{{ help }} <VbenRenderContent :content="help" />
</VbenHelpTooltip> </VbenHelpTooltip>
</FormLabel> </FormLabel>
</template> </template>

View File

@ -244,13 +244,13 @@ export interface FormSchema<
/** 依赖 */ /** 依赖 */
dependencies?: FormItemDependencies; dependencies?: FormItemDependencies;
/** 描述 */ /** 描述 */
description?: string; description?: CustomRenderType;
/** 字段名 */ /** 字段名 */
fieldName: string; fieldName: string;
/** 帮助信息 */ /** 帮助信息 */
help?: string; help?: CustomRenderType;
/** 表单项 */ /** 表单项 */
label?: string; label?: CustomRenderType;
// 自定义组件内部渲染 // 自定义组件内部渲染
renderComponentContent?: RenderComponentContentType; renderComponentContent?: RenderComponentContentType;
/** 字段规则 */ /** 字段规则 */

View File

@ -4,10 +4,18 @@ import { h, ref } from 'vue';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { useDebounceFn } from '@vueuse/core'; import { useDebounceFn } from '@vueuse/core';
import { Button, Card, message, Spin, TabPane, Tabs } from 'ant-design-vue'; import {
Button,
Card,
message,
Spin,
TabPane,
Tabs,
Tag,
} from 'ant-design-vue';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm, z } from '#/adapter/form';
import { getAllMenusApi } from '#/api'; import { getAllMenusApi } from '#/api';
import DocButton from '../doc-button.vue'; import DocButton from '../doc-button.vue';
@ -111,6 +119,7 @@ const [BaseForm, baseFormApi] = useVbenForm({
notFoundContent: fetching.value ? h(Spin) : undefined, notFoundContent: fetching.value ? h(Spin) : undefined,
}; };
}, },
rules: 'selectRequired',
}, },
{ {
component: 'ApiTreeSelect', component: 'ApiTreeSelect',
@ -151,6 +160,7 @@ const [BaseForm, baseFormApi] = useVbenForm({
label: '图标', label: '图标',
}, },
{ {
colon: false,
component: 'Select', component: 'Select',
componentProps: { componentProps: {
allowClear: true, allowClear: true,
@ -169,7 +179,7 @@ const [BaseForm, baseFormApi] = useVbenForm({
showSearch: true, showSearch: true,
}, },
fieldName: 'options', fieldName: 'options',
label: '下拉选', label: () => h(Tag, { color: 'warning' }, () => '😎自定义:'),
}, },
{ {
component: 'RadioGroup', component: 'RadioGroup',
@ -225,6 +235,7 @@ const [BaseForm, baseFormApi] = useVbenForm({
default: () => ['我已阅读并同意'], default: () => ['我已阅读并同意'],
}; };
}, },
rules: z.any().refine((v) => v, { message: '为什么不同意?勾上它!' }),
}, },
{ {
component: 'Mentions', component: 'Mentions',
@ -255,6 +266,7 @@ const [BaseForm, baseFormApi] = useVbenForm({
class: 'w-auto', class: 'w-auto',
}, },
fieldName: 'switch', fieldName: 'switch',
help: () => ['这是一个帮助信息', '第二行'].map((v) => h('p', () => v)),
label: '开关', label: '开关',
}, },
{ {