feat: add VbenForm component (#4352)

* feat: add form component

* fix: build error

* feat: add form adapter

* feat: add some component

* feat: add some component

* feat: add example

* feat: suppoer custom action button

* chore: update

* feat: add example

* feat: add formModel,formDrawer demo

* fix: build error

* fix: typo

* fix: ci error

---------

Co-authored-by: jinmao <jinmao88@qq.com>
Co-authored-by: likui628 <90845831+likui628@users.noreply.github.com>
This commit is contained in:
Vben
2024-09-10 21:48:51 +08:00
committed by GitHub
parent 86ed732ca8
commit 524b9badf2
271 changed files with 5974 additions and 1247 deletions

View File

@@ -5,7 +5,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
import { DrawerApi } from '../drawer-api';
// 模拟 Store 类
vi.mock('@vben-core/shared', () => {
vi.mock('@vben-core/shared/store', () => {
return {
isFunction: (fn: any) => typeof fn === 'function',
Store: class {

View File

@@ -1,6 +1,7 @@
import type { DrawerApiOptions, DrawerState } from './drawer';
import { isFunction, Store } from '@vben-core/shared';
import { Store } from '@vben-core/shared/store';
import { bindMethods, isFunction } from '@vben-core/shared/utils';
export class DrawerApi {
private api: Pick<
@@ -58,13 +59,14 @@ export class DrawerApi {
},
},
);
this.state = this.store.state;
this.api = {
onBeforeClose,
onCancel,
onConfirm,
onOpenChange,
};
bindMethods(this);
}
// 如果需要多次更新状态,可以使用 batch 方法

View File

@@ -5,10 +5,10 @@ import { ref, watch } from 'vue';
import {
useIsMobile,
usePriorityValue,
usePriorityValues,
useSimpleLocale,
} from '@vben-core/composables';
import { Info, X } from '@vben-core/icons';
import { X } from '@vben-core/icons';
import {
Sheet,
SheetClose,
@@ -18,12 +18,12 @@ import {
SheetHeader,
SheetTitle,
VbenButton,
VbenHelpTooltip,
VbenIconButton,
VbenLoading,
VbenTooltip,
VisuallyHidden,
} from '@vben-core/shadcn-ui';
import { cn } from '@vben-core/shared';
import { cn } from '@vben-core/shared/utils';
interface Props extends DrawerProps {
class?: string;
@@ -42,20 +42,22 @@ const { $t } = useSimpleLocale();
const { isMobile } = useIsMobile();
const state = props.drawerApi?.useStore?.();
const title = usePriorityValue('title', props, state);
const description = usePriorityValue('description', props, state);
const titleTooltip = usePriorityValue('titleTooltip', props, state);
const showFooter = usePriorityValue('footer', props, state);
const showLoading = usePriorityValue('loading', props, state);
const closable = usePriorityValue('closable', props, state);
const modal = usePriorityValue('modal', props, state);
const confirmLoading = usePriorityValue('confirmLoading', props, state);
const cancelText = usePriorityValue('cancelText', props, state);
const confirmText = usePriorityValue('confirmText', props, state);
const closeOnClickModal = usePriorityValue('closeOnClickModal', props, state);
const closeOnPressEscape = usePriorityValue('closeOnPressEscape', props, state);
const showCancelButton = usePriorityValue('showCancelButton', props, state);
const showConfirmButton = usePriorityValue('showConfirmButton', props, state);
const {
cancelText,
closable,
closeOnClickModal,
closeOnPressEscape,
confirmLoading,
confirmText,
description,
footer: showFooter,
loading: showLoading,
modal,
showCancelButton,
showConfirmButton,
title,
titleTooltip,
} = usePriorityValues(props, state);
watch(
() => showLoading.value,
@@ -116,12 +118,9 @@ function pointerDownOutside(e: Event) {
<slot name="title">
{{ title }}
<VbenTooltip v-if="titleTooltip" side="right">
<template #trigger>
<Info class="inline-flex size-5 cursor-pointer pb-1" />
</template>
<VbenHelpTooltip v-if="titleTooltip" trigger-class="pb-1">
{{ titleTooltip }}
</VbenTooltip>
</VbenHelpTooltip>
</slot>
</SheetTitle>
<SheetDescription v-if="description" class="mt-1 text-xs">

View File

@@ -6,7 +6,7 @@ import type {
import { defineComponent, h, inject, nextTick, provide, reactive } from 'vue';
import { useStore } from '@vben-core/shared';
import { useStore } from '@vben-core/shared/store';
import VbenDrawer from './drawer.vue';
import { DrawerApi } from './drawer-api';

View File

@@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
import { ModalApi } from '../modal-api'; // 假设 ModalApi 位于同一目录
import type { ModalState } from '../modal';
vi.mock('@vben-core/shared', () => {
vi.mock('@vben-core/shared/store', () => {
return {
isFunction: (fn: any) => typeof fn === 'function',
Store: class {

View File

@@ -1,6 +1,7 @@
import type { ModalApiOptions, ModalState } from './modal';
import { isFunction, Store } from '@vben-core/shared';
import { Store } from '@vben-core/shared/store';
import { bindMethods, isFunction } from '@vben-core/shared/utils';
export class ModalApi {
private api: Pick<
@@ -65,12 +66,15 @@ export class ModalApi {
},
);
this.state = this.store.state;
this.api = {
onBeforeClose,
onCancel,
onConfirm,
onOpenChange,
};
bindMethods(this);
}
// 如果需要多次更新状态,可以使用 batch 方法

View File

@@ -5,10 +5,10 @@ import { computed, nextTick, ref, watch } from 'vue';
import {
useIsMobile,
usePriorityValue,
usePriorityValues,
useSimpleLocale,
} from '@vben-core/composables';
import { Expand, Info, Shrink } from '@vben-core/icons';
import { Expand, Shrink } from '@vben-core/icons';
import {
Dialog,
DialogContent,
@@ -17,12 +17,12 @@ import {
DialogHeader,
DialogTitle,
VbenButton,
VbenHelpTooltip,
VbenIconButton,
VbenLoading,
VbenTooltip,
VisuallyHidden,
} from '@vben-core/shadcn-ui';
import { cn } from '@vben-core/shared';
import { cn } from '@vben-core/shared/utils';
import { useModalDraggable } from './use-modal-draggable';
@@ -52,25 +52,27 @@ const { $t } = useSimpleLocale();
const { isMobile } = useIsMobile();
const state = props.modalApi?.useStore?.();
const header = usePriorityValue('header', props, state);
const title = usePriorityValue('title', props, state);
const fullscreen = usePriorityValue('fullscreen', props, state);
const description = usePriorityValue('description', props, state);
const titleTooltip = usePriorityValue('titleTooltip', props, state);
const showFooter = usePriorityValue('footer', props, state);
const showLoading = usePriorityValue('loading', props, state);
const closable = usePriorityValue('closable', props, state);
const modal = usePriorityValue('modal', props, state);
const centered = usePriorityValue('centered', props, state);
const confirmLoading = usePriorityValue('confirmLoading', props, state);
const cancelText = usePriorityValue('cancelText', props, state);
const confirmText = usePriorityValue('confirmText', props, state);
const draggable = usePriorityValue('draggable', props, state);
const fullscreenButton = usePriorityValue('fullscreenButton', props, state);
const closeOnClickModal = usePriorityValue('closeOnClickModal', props, state);
const closeOnPressEscape = usePriorityValue('closeOnPressEscape', props, state);
const showCancelButton = usePriorityValue('showCancelButton', props, state);
const showConfirmButton = usePriorityValue('showConfirmButton', props, state);
const {
cancelText,
centered,
closable,
closeOnClickModal,
closeOnPressEscape,
confirmLoading,
confirmText,
description,
draggable,
footer: showFooter,
fullscreen,
fullscreenButton,
header,
loading: showLoading,
modal,
showCancelButton,
showConfirmButton,
title,
titleTooltip,
} = usePriorityValues(props, state);
const shouldFullscreen = computed(
() => (fullscreen.value && header.value) || isMobile.value,
@@ -184,12 +186,9 @@ function pointerDownOutside(e: Event) {
{{ title }}
<slot v-if="titleTooltip" name="titleTooltip">
<VbenTooltip side="right">
<template #trigger>
<Info class="inline-flex size-5 cursor-pointer pb-1" />
</template>
<VbenHelpTooltip trigger-class="pb-1">
{{ titleTooltip }}
</VbenTooltip>
</VbenHelpTooltip>
</slot>
</slot>
</DialogTitle>

View File

@@ -2,7 +2,7 @@ import type { ExtendedModalApi, ModalApiOptions, ModalProps } from './modal';
import { defineComponent, h, inject, nextTick, provide, reactive } from 'vue';
import { useStore } from '@vben-core/shared';
import { useStore } from '@vben-core/shared/store';
import VbenModal from './modal.vue';
import { ModalApi } from './modal-api';
@@ -33,7 +33,15 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
...attrs,
...slots,
});
return () => h(connectedComponent, { ...props, ...attrs }, slots);
return () =>
h(
connectedComponent,
{
...props,
...attrs,
},
slots,
);
},
{
inheritAttrs: false,
@@ -65,7 +73,15 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
const Modal = defineComponent(
(props: ModalProps, { attrs, slots }) => {
return () =>
h(VbenModal, { ...props, ...attrs, modalApi: extendedApi }, slots);
h(
VbenModal,
{
...props,
...attrs,
modalApi: extendedApi,
},
slots,
);
},
{
inheritAttrs: false,