Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 4 additions & 23 deletions packages/@core/ui-kit/form-ui/src/form-render/form-field.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import type { FormSchema, MaybeComponentProps } from '../types';

import { computed, nextTick, onUnmounted, useTemplateRef, watch } from 'vue';

import { CircleAlert } from '@vben-core/icons';
import {
FormControl,
FormDescription,
FormField,
FormItem,
FormMessage,
VbenRenderContent,
VbenTooltip,
} from '@vben-core/shadcn-ui';
import { cn, isFunction, isObject, isString } from '@vben-core/shared/utils';

Expand Down Expand Up @@ -323,7 +321,7 @@ onUnmounted(() => {
<VbenRenderContent :content="label" />
</template>
</FormLabel>
<div class="flex-auto overflow-hidden p-[1px]">
<div class="relative flex-auto p-[1px]">
<div :class="cn('relative flex w-full items-center', wrapperClass)">
<FormControl :class="cn(controlClass)">
<slot
Expand All @@ -340,6 +338,7 @@ onUnmounted(() => {
:class="{
'border-destructive focus:border-destructive hover:border-destructive/80 focus:shadow-[0_0_0_2px_rgba(255,38,5,0.06)]':
isInValid,
'relative hover:z-10 focus:z-10': compact,
}"
v-bind="createComponentProps(slotProps)"
:disabled="shouldDisabled"
Expand All @@ -356,24 +355,6 @@ onUnmounted(() => {
</template>
<!-- <slot></slot> -->
</component>
<VbenTooltip
v-if="compact && isInValid"
:delay-duration="300"
side="left"
>
<template #trigger>
<slot name="trigger">
<CircleAlert
:class="
cn(
'text-foreground/80 hover:text-foreground inline-flex size-5 cursor-pointer',
)
"
/>
</slot>
</template>
<FormMessage />
</VbenTooltip>
</slot>
</FormControl>
<!-- 自定义后缀 -->
Expand All @@ -385,8 +366,8 @@ onUnmounted(() => {
</FormDescription>
</div>

<Transition name="slide-up" v-if="!compact">
<FormMessage class="absolute" />
<Transition name="slide-up">
<FormMessage :compact="compact" class="absolute" />
</Transition>
Comment on lines +369 to 371
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: class="absolute" forces non-compact messages to be absolutely positioned.

FormMessage already applies absolute positioning internally in compact mode. Passing class="absolute" here unconditionally makes the non-compact version also absolute, breaking the default layout and likely causing layout shifts.

Apply this diff to let FormMessage control positioning:

-        <Transition name="slide-up">
-          <FormMessage :compact="compact" class="absolute" />
-        </Transition>
+        <Transition name="slide-up">
+          <FormMessage :compact="compact" />
+        </Transition>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Transition name="slide-up">
<FormMessage :compact="compact" class="absolute" />
</Transition>
<Transition name="slide-up">
<FormMessage :compact="compact" />
</Transition>
🤖 Prompt for AI Agents
In packages/@core/ui-kit/form-ui/src/form-render/form-field.vue around lines 369
to 371, the FormMessage is being forced to absolute positioning by passing
class="absolute" unconditionally; remove that hardcoded class (or change it to
only apply when compact is true) so FormMessage can control its own positioning
internally — e.g. delete the class="absolute" attribute or replace it with a
conditional class bound to the compact prop so only compact messages become
absolute.

</div>
</FormItem>
Expand Down
37 changes: 35 additions & 2 deletions packages/@core/ui-kit/shadcn-ui/src/ui/form/FormMessage.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,51 @@
<script lang="ts" setup>
import { toValue } from 'vue';

import { cn } from '@vben-core/shared/utils';

import { ErrorMessage } from 'vee-validate';

import { useFormField } from './useFormField';

interface Props {
compact?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
compact: false,
});

const { formMessageId, name } = useFormField();
</script>

<template>
<ErrorMessage
:id="formMessageId"
:name="toValue(name)"
as="p"
class="text-destructive text-[0.8rem]"
:as="props.compact ? 'div' : 'p'"
:class="
cn(
'text-[0.8rem]',
props.compact ? 'vben-form-message-compact' : 'text-destructive',
)
"
/>
</template>

<style>
.vben-form-message-compact {
position: absolute;
top: 100%;
left: 1rem;
z-index: 1;
width: calc(100% - 1rem);
height: auto;
min-height: 1.5rem;
padding: 2px 8px;
color: hsl(var(--destructive-foreground));
pointer-events: none;
background: hsl(var(--destructive));
border-radius: var(--radius);
opacity: 1;
}
</style>
11 changes: 8 additions & 3 deletions playground/src/views/examples/form/rules.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
<script lang="ts" setup>
import { ref } from 'vue';

import { Page } from '@vben/common-ui';

import { Button, Card, message } from 'ant-design-vue';

import { useVbenForm, z } from '#/adapter/form';

const isCompact = ref(true);

const [Form, formApi] = useVbenForm({
// 所有表单项共用,可单独在表单内覆盖
commonConfig: {
Expand All @@ -17,7 +21,7 @@ const [Form, formApi] = useVbenForm({
handleSubmit: onSubmit,
// 垂直布局,label和input在不同行,值为vertical
// 水平布局,label和input在同一行
layout: 'horizontal',
// layout: 'horizontal',
schema: [
{
// 组件需要在 #/adapter.ts内注册,并加上类型
Expand Down Expand Up @@ -234,12 +238,13 @@ function onSubmit(values: Record<string, any>) {
<Page description="表单校验示例" title="表单组件">
<Card title="基础组件校验示例">
<template #extra>
<Button @click="() => formApi.validate()">校验表单</Button>
<Button @click="isCompact = !isCompact">切换Compact</Button>
<Button class="mx-2" @click="() => formApi.validate()">校验表单</Button>
<Button class="mx-2" @click="() => formApi.resetValidate()">
清空校验信息
</Button>
</template>
<Form />
<Form :compact="isCompact" />
</Card>
</Page>
</template>
Loading