浏览代码

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
Netfan 1 月之前
父节点
当前提交
a221d2b491

+ 3 - 3
docs/src/components/common-ui/vben-form.md

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

+ 3 - 1
docs/src/guide/essentials/settings.md

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

+ 47 - 43
packages/@core/ui-kit/form-ui/src/form-render/form-field.vue

@@ -193,7 +193,7 @@ const fieldProps = computed(() => {
   const rules = fieldRules.value;
   return {
     keepValue: true,
-    label,
+    label: isString(label) ? label : '',
     ...(rules ? { rules } : {}),
     ...(formFieldProps as Record<string, any>),
   };
@@ -285,7 +285,7 @@ function autofocus() {
         'pb-6': !compact,
         'pb-2': compact,
       }"
-      class="flex"
+      class="relative flex"
       v-bind="$attrs"
     >
       <FormLabel
@@ -305,55 +305,59 @@ function autofocus() {
         :style="labelStyle"
       >
         <template v-if="label">
-          <span>{{ label }}</span>
+          <VbenRenderContent :content="label" />
           <span v-if="colon" class="ml-[2px]">:</span>
         </template>
       </FormLabel>
-      <div :class="cn('relative flex w-full items-center', wrapperClass)">
-        <FormControl :class="cn(controlClass)">
-          <slot
-            v-bind="{
-              ...slotProps,
-              ...createComponentProps(slotProps),
-              disabled: shouldDisabled,
-              isInValid,
-            }"
-          >
-            <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)]':
+      <div class="w-full overflow-hidden">
+        <div :class="cn('relative flex w-full items-center', wrapperClass)">
+          <div class="flex-auto overflow-hidden">
+            <FormControl :class="cn(controlClass)">
+              <slot
+                v-bind="{
+                  ...slotProps,
+                  ...createComponentProps(slotProps),
+                  disabled: shouldDisabled,
                   isInValid,
-              }"
-              v-bind="createComponentProps(slotProps)"
-              :disabled="shouldDisabled"
-            >
-              <template
-                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 v-if="suffix" class="ml-1">
-          <VbenRenderContent :content="suffix" />
+                <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,
+                  }"
+                  v-bind="createComponentProps(slotProps)"
+                  :disabled="shouldDisabled"
+                >
+                  <template
+                    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>
 
-        <FormDescription v-if="description">
-          <VbenRenderContent :content="description" />
-        </FormDescription>
-
         <Transition name="slide-up">
-          <FormMessage class="absolute -bottom-[22px]" />
+          <FormMessage class="absolute bottom-1" />
         </Transition>
       </div>
     </FormItem>

+ 9 - 3
packages/@core/ui-kit/form-ui/src/form-render/form-label.vue

@@ -1,10 +1,16 @@
 <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';
 
 interface Props {
   class?: string;
-  help?: string;
+  help?: CustomRenderType;
   required?: boolean;
 }
 
@@ -16,7 +22,7 @@ const props = defineProps<Props>();
     <span v-if="required" class="text-destructive mr-[2px]">*</span>
     <slot></slot>
     <VbenHelpTooltip v-if="help" trigger-class="size-3.5 ml-1">
-      {{ help }}
+      <VbenRenderContent :content="help" />
     </VbenHelpTooltip>
   </FormLabel>
 </template>

+ 3 - 3
packages/@core/ui-kit/form-ui/src/types.ts

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

+ 15 - 3
playground/src/views/examples/form/basic.vue

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