diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/button/check-button-group.vue b/packages/@core/ui-kit/shadcn-ui/src/components/button/check-button-group.vue
index fd87396e..4172d82b 100644
--- a/packages/@core/ui-kit/shadcn-ui/src/components/button/check-button-group.vue
+++ b/packages/@core/ui-kit/shadcn-ui/src/components/button/check-button-group.vue
@@ -122,6 +122,7 @@ async function onBtnClick(value: ValueType) {
v-bind="btnDefaultProps"
:variant="innerValue.includes(btn.value) ? 'default' : 'outline'"
@click="onBtnClick(btn.value)"
+ type="button"
>
number) | number;
+ /** 鼠标离开延迟时间 */
+ leaveDelay?: (() => number) | number;
+}
+
+const DEFAULT_LEAVE_DELAY = 500; // 鼠标离开延迟时间,默认为 500ms
+const DEFAULT_ENTER_DELAY = 0; // 鼠标进入延迟时间,默认为 0(立即响应)
+
/**
* 监测鼠标是否在元素内部,如果在元素内部则返回 true,否则返回 false
* @param refElement 所有需要检测的元素。如果提供了一个数组,那么鼠标在任何一个元素内部都会返回 true
- * @param delay 延迟更新状态的时间
+ * @param delay 延迟更新状态的时间,可以是数字或包含进入/离开延迟的配置对象
* @returns 返回一个数组,第一个元素是一个 ref,表示鼠标是否在元素内部,第二个元素是一个控制器,可以通过 enable 和 disable 方法来控制监听器的启用和禁用
*/
export function useHoverToggle(
refElement: Arrayable,
- delay: (() => number) | number = 500,
+ delay: (() => number) | HoverDelayOptions | number = DEFAULT_LEAVE_DELAY,
) {
+ // 兼容旧版本API
+ const normalizedOptions: HoverDelayOptions =
+ typeof delay === 'number' || isFunction(delay)
+ ? { enterDelay: DEFAULT_ENTER_DELAY, leaveDelay: delay }
+ : {
+ enterDelay: DEFAULT_ENTER_DELAY,
+ leaveDelay: DEFAULT_LEAVE_DELAY,
+ ...delay,
+ };
+
const isHovers: Array[> = [];
const value = ref(false);
- const timer = ref | undefined>();
+ const enterTimer = ref | undefined>();
+ const leaveTimer = ref | undefined>();
const refs = Array.isArray(refElement) ? refElement : [refElement];
refs.forEach((refEle) => {
const eleRef = computed(() => {
@@ -32,15 +53,47 @@ export function useHoverToggle(
});
const isOutsideAll = computed(() => isHovers.every((v) => !v.value));
+ function clearTimers() {
+ if (enterTimer.value) {
+ clearTimeout(enterTimer.value);
+ enterTimer.value = undefined;
+ }
+ if (leaveTimer.value) {
+ clearTimeout(leaveTimer.value);
+ leaveTimer.value = undefined;
+ }
+ }
+
function setValueDelay(val: boolean) {
- timer.value && clearTimeout(timer.value);
- timer.value = setTimeout(
- () => {
- value.value = val;
- timer.value = undefined;
- },
- isFunction(delay) ? delay() : delay,
- );
+ clearTimers();
+
+ if (val) {
+ // 鼠标进入
+ const enterDelay = normalizedOptions.enterDelay ?? DEFAULT_ENTER_DELAY;
+ const delayTime = isFunction(enterDelay) ? enterDelay() : enterDelay;
+
+ if (delayTime <= 0) {
+ value.value = true;
+ } else {
+ enterTimer.value = setTimeout(() => {
+ value.value = true;
+ enterTimer.value = undefined;
+ }, delayTime);
+ }
+ } else {
+ // 鼠标离开
+ const leaveDelay = normalizedOptions.leaveDelay ?? DEFAULT_LEAVE_DELAY;
+ const delayTime = isFunction(leaveDelay) ? leaveDelay() : leaveDelay;
+
+ if (delayTime <= 0) {
+ value.value = false;
+ } else {
+ leaveTimer.value = setTimeout(() => {
+ value.value = false;
+ leaveTimer.value = undefined;
+ }, delayTime);
+ }
+ }
}
const watcher = watch(
@@ -61,7 +114,7 @@ export function useHoverToggle(
};
onUnmounted(() => {
- timer.value && clearTimeout(timer.value);
+ clearTimers();
});
return [value, controller] as [typeof value, typeof controller];
diff --git a/packages/effects/plugins/src/vxe-table/api.ts b/packages/effects/plugins/src/vxe-table/api.ts
index 133dbb33..2b60d602 100644
--- a/packages/effects/plugins/src/vxe-table/api.ts
+++ b/packages/effects/plugins/src/vxe-table/api.ts
@@ -1,8 +1,11 @@
-import type { ExtendedFormApi } from '@vben-core/form-ui';
import type { VxeGridInstance } from 'vxe-table';
+import type { ExtendedFormApi } from '@vben-core/form-ui';
+
import type { VxeGridProps } from './types';
+import { toRaw } from 'vue';
+
import { Store } from '@vben-core/shared/store';
import {
bindMethods,
@@ -11,7 +14,6 @@ import {
mergeWithArrayOverride,
StateHandler,
} from '@vben-core/shared/utils';
-import { toRaw } from 'vue';
function getDefaultState(): VxeGridProps {
return {
@@ -24,18 +26,18 @@ function getDefaultState(): VxeGridProps {
};
}
-export class VxeGridApi {
- private isMounted = false;
-
- private stateHandler: StateHandler;
+export class VxeGridApi = any> {
public formApi = {} as ExtendedFormApi;
// private prevState: null | VxeGridProps = null;
- public grid = {} as VxeGridInstance;
+ public grid = {} as VxeGridInstance;
+ public state: null | VxeGridProps = null;
- public state: null | VxeGridProps = null;
+ public store: Store>;
- public store: Store;
+ private isMounted = false;
+
+ private stateHandler: StateHandler;
constructor(options: VxeGridProps = {}) {
const storeState = { ...options };
@@ -97,8 +99,8 @@ export class VxeGridApi {
setState(
stateOrFn:
- | ((prev: VxeGridProps) => Partial)
- | Partial,
+ | ((prev: VxeGridProps) => Partial>)
+ | Partial>,
) {
if (isFunction(stateOrFn)) {
this.store.setState((prev) => {
diff --git a/packages/effects/plugins/src/vxe-table/types.ts b/packages/effects/plugins/src/vxe-table/types.ts
index 09c2a8ee..8b9aea47 100644
--- a/packages/effects/plugins/src/vxe-table/types.ts
+++ b/packages/effects/plugins/src/vxe-table/types.ts
@@ -9,7 +9,7 @@ import type { Ref } from 'vue';
import type { ClassType, DeepPartial } from '@vben/types';
-import type { VbenFormProps } from '@vben-core/form-ui';
+import type { BaseFormComponentType, VbenFormProps } from '@vben-core/form-ui';
import type { VxeGridApi } from './api';
@@ -35,7 +35,11 @@ export interface SeparatorOptions {
show?: boolean;
backgroundColor?: string;
}
-export interface VxeGridProps {
+
+export interface VxeGridProps<
+ T extends Record = any,
+ D extends BaseFormComponentType = BaseFormComponentType,
+> {
/**
* 标题
*/
@@ -55,15 +59,15 @@ export interface VxeGridProps {
/**
* vxe-grid 配置
*/
- gridOptions?: DeepPartial;
+ gridOptions?: DeepPartial>;
/**
* vxe-grid 事件
*/
- gridEvents?: Partial;
+ gridEvents?: DeepPartial>;
/**
* 表单配置
*/
- formOptions?: VbenFormProps;
+ formOptions?: VbenFormProps;
/**
* 显示搜索表单
*/
@@ -74,9 +78,12 @@ export interface VxeGridProps {
separator?: boolean | SeparatorOptions;
}
-export type ExtendedVxeGridApi = VxeGridApi & {
- useStore: >(
- selector?: (state: NoInfer) => T,
+export type ExtendedVxeGridApi<
+ D extends Record = any,
+ F extends BaseFormComponentType = BaseFormComponentType,
+> = VxeGridApi & {
+ useStore: >>(
+ selector?: (state: NoInfer>) => T,
) => Readonly][>;
};
diff --git a/packages/effects/plugins/src/vxe-table/use-vxe-grid.ts b/packages/effects/plugins/src/vxe-table/use-vxe-grid.ts
index b15435ed..e69ae35b 100644
--- a/packages/effects/plugins/src/vxe-table/use-vxe-grid.ts
+++ b/packages/effects/plugins/src/vxe-table/use-vxe-grid.ts
@@ -1,3 +1,5 @@
+import type { BaseFormComponentType } from '@vben-core/form-ui';
+
import type { ExtendedVxeGridApi, VxeGridProps } from './types';
import { defineComponent, h, onBeforeUnmount } from 'vue';
@@ -7,16 +9,19 @@ import { useStore } from '@vben-core/shared/store';
import { VxeGridApi } from './api';
import VxeGrid from './use-vxe-grid.vue';
-export function useVbenVxeGrid(options: VxeGridProps) {
+export function useVbenVxeGrid<
+ T extends Record = any,
+ D extends BaseFormComponentType = BaseFormComponentType,
+>(options: VxeGridProps) {
// const IS_REACTIVE = isReactive(options);
const api = new VxeGridApi(options);
- const extendedApi: ExtendedVxeGridApi = api as ExtendedVxeGridApi;
+ const extendedApi: ExtendedVxeGridApi = api as ExtendedVxeGridApi;
extendedApi.useStore = (selector) => {
return useStore(api.store, selector);
};
const Grid = defineComponent(
- (props: VxeGridProps, { attrs, slots }) => {
+ (props: VxeGridProps, { attrs, slots }) => {
onBeforeUnmount(() => {
api.unmount();
});
diff --git a/packages/effects/request/src/request-client/modules/uploader.ts b/packages/effects/request/src/request-client/modules/uploader.ts
index de251ca8..1353222a 100644
--- a/packages/effects/request/src/request-client/modules/uploader.ts
+++ b/packages/effects/request/src/request-client/modules/uploader.ts
@@ -1,6 +1,8 @@
import type { RequestClient } from '../request-client';
import type { RequestClientConfig } from '../types';
+import { isUndefined } from '@vben/utils';
+
class FileUploader {
private client: RequestClient;
@@ -18,10 +20,10 @@ class FileUploader {
Object.entries(data).forEach(([key, value]) => {
if (Array.isArray(value)) {
value.forEach((item, index) => {
- formData.append(`${key}[${index}]`, item);
+ !isUndefined(item) && formData.append(`${key}[${index}]`, item);
});
} else {
- formData.append(key, value);
+ !isUndefined(value) && formData.append(key, value);
}
});
diff --git a/playground/src/adapter/vxe-table.ts b/playground/src/adapter/vxe-table.ts
index 71a0ecb4..24dfd4cd 100644
--- a/playground/src/adapter/vxe-table.ts
+++ b/playground/src/adapter/vxe-table.ts
@@ -1,10 +1,16 @@
+import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
import type { Recordable } from '@vben/types';
+import type { ComponentType } from './component';
+
import { h } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { $te } from '@vben/locales';
-import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table';
+import {
+ setupVbenVxeTable,
+ useVbenVxeGrid as useGrid,
+} from '@vben/plugins/vxe-table';
import { get, isFunction, isString } from '@vben/utils';
import { objectOmit } from '@vueuse/core';
@@ -42,7 +48,7 @@ setupVbenVxeTable({
round: true,
showOverflow: true,
size: 'small',
- },
+ } as VxeTableGridOptions,
});
/**
@@ -277,7 +283,10 @@ setupVbenVxeTable({
useVbenForm,
});
-export { useVbenVxeGrid };
+export const useVbenVxeGrid = >(
+ ...rest: Parameters>
+) => useGrid(...rest);
+
export type OnActionClickParams> = {
code: string;
row: T;
diff --git a/playground/src/views/examples/vxe-table/basic.vue b/playground/src/views/examples/vxe-table/basic.vue
index 1216468d..d2047246 100644
--- a/playground/src/views/examples/vxe-table/basic.vue
+++ b/playground/src/views/examples/vxe-table/basic.vue
@@ -43,7 +43,22 @@ const gridEvents: VxeGridListeners = {
},
};
-const [Grid, gridApi] = useVbenVxeGrid({ gridEvents, gridOptions });
+const [Grid, gridApi] = useVbenVxeGrid({
+ // 放开注释查看表单组件的类型
+ // formOptions: {
+ // schema: [
+ // {
+ // component: 'Switch',
+ // fieldName: 'name',
+ // },
+ // ],
+ // },
+ gridEvents,
+ gridOptions,
+});
+
+// 放开注释查看当前表格实例的类型
+// gridApi.grid
const showBorder = gridApi.useStore((state) => state.gridOptions?.border);
const showStripe = gridApi.useStore((state) => state.gridOptions?.stripe);
diff --git a/playground/src/views/system/menu/modules/form.vue b/playground/src/views/system/menu/modules/form.vue
index 6701a2e5..3cf40e35 100644
--- a/playground/src/views/system/menu/modules/form.vue
+++ b/playground/src/views/system/menu/modules/form.vue
@@ -241,10 +241,10 @@ const schema: VbenFormSchema[] = [
component: 'Input',
dependencies: {
rules: (values) => {
- return values.type === 'action' ? 'required' : null;
+ return values.type === 'button' ? 'required' : null;
},
show: (values) => {
- return ['action', 'catalog', 'embedded', 'menu'].includes(values.type);
+ return ['button', 'catalog', 'embedded', 'menu'].includes(values.type);
},
triggerFields: ['type'],
},
@@ -277,7 +277,7 @@ const schema: VbenFormSchema[] = [
},
dependencies: {
show: (values) => {
- return values.type !== 'action';
+ return values.type !== 'button';
},
triggerFields: ['type'],
},
@@ -295,7 +295,7 @@ const schema: VbenFormSchema[] = [
},
dependencies: {
show: (values) => {
- return values.type !== 'action';
+ return values.type !== 'button';
},
triggerFields: ['type'],
},
@@ -314,7 +314,7 @@ const schema: VbenFormSchema[] = [
},
dependencies: {
show: (values) => {
- return values.type !== 'action';
+ return values.type !== 'button';
},
triggerFields: ['type'],
},
@@ -325,7 +325,7 @@ const schema: VbenFormSchema[] = [
component: 'Divider',
dependencies: {
show: (values) => {
- return !['action', 'link'].includes(values.type);
+ return !['button', 'link'].includes(values.type);
},
triggerFields: ['type'],
},
@@ -372,7 +372,7 @@ const schema: VbenFormSchema[] = [
component: 'Checkbox',
dependencies: {
show: (values) => {
- return !['action'].includes(values.type);
+ return !['button'].includes(values.type);
},
triggerFields: ['type'],
},
@@ -402,7 +402,7 @@ const schema: VbenFormSchema[] = [
component: 'Checkbox',
dependencies: {
show: (values) => {
- return !['action', 'link'].includes(values.type);
+ return !['button', 'link'].includes(values.type);
},
triggerFields: ['type'],
},
@@ -417,7 +417,7 @@ const schema: VbenFormSchema[] = [
component: 'Checkbox',
dependencies: {
show: (values) => {
- return !['action', 'link'].includes(values.type);
+ return !['button', 'link'].includes(values.type);
},
triggerFields: ['type'],
},
]