This commit is contained in:
dap
2024-12-17 08:01:02 +08:00
101 changed files with 853 additions and 193 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/access",
"version": "5.5.0",
"version": "5.5.1",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/common-ui",
"version": "5.5.0",
"version": "5.5.1",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {

View File

@@ -1,21 +1,23 @@
<script setup lang="ts">
import type { AuthenticationProps } from './types';
import { watch } from 'vue';
import { computed, watch } from 'vue';
import { useVbenModal } from '@vben-core/popup-ui';
import { Slot, VbenAvatar } from '@vben-core/shadcn-ui';
interface Props extends AuthenticationProps {
avatar?: string;
zIndex?: number;
}
defineOptions({
name: 'LoginExpiredModal',
});
withDefaults(defineProps<Props>(), {
const props = withDefaults(defineProps<Props>(), {
avatar: '',
zIndex: 0,
});
const open = defineModel<boolean>('open');
@@ -28,6 +30,26 @@ watch(
modalApi.setState({ isOpen: val });
},
);
const getZIndex = computed(() => {
return props.zIndex || calcZIndex();
});
/**
* 获取最大的zIndex值
*/
function calcZIndex() {
let maxZ = 0;
const elements = document.querySelectorAll('*');
[...elements].forEach((element) => {
const style = window.getComputedStyle(element);
const zIndex = style.getPropertyValue('z-index');
if (zIndex && !Number.isNaN(Number.parseInt(zIndex))) {
maxZ = Math.max(maxZ, Number.parseInt(zIndex));
}
});
return maxZ + 1;
}
</script>
<template>
@@ -39,6 +61,7 @@ watch(
:footer="false"
:fullscreen-button="false"
:header="false"
:z-index="getZIndex"
class="border-none px-10 py-6 text-center shadow-xl sm:w-[600px] sm:rounded-2xl md:h-[unset]"
>
<VbenAvatar :src="avatar" class="mx-auto mb-6 size-20" />

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/hooks",
"version": "5.5.0",
"version": "5.5.1",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {
@@ -25,6 +25,7 @@
"@vben/stores": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"@vueuse/core": "catalog:",
"vue": "catalog:",
"vue-router": "catalog:",
"watermark-js-plus": "catalog:"

View File

@@ -1,6 +1,7 @@
export * from './use-app-config';
export * from './use-content-maximize';
export * from './use-design-tokens';
export * from './use-hover-toggle';
export * from './use-pagination';
export * from './use-refresh';
export * from './use-tabs';

View File

@@ -0,0 +1,63 @@
import type { Arrayable, MaybeElementRef } from '@vueuse/core';
import { computed, onUnmounted, ref, watch } from 'vue';
import type { Ref } from 'vue';
import { isFunction } from '@vben/utils';
import { useMouseInElement } from '@vueuse/core';
/**
* 监测鼠标是否在元素内部,如果在元素内部则返回 true否则返回 false
* @param refElement 所有需要检测的元素。如果提供了一个数组,那么鼠标在任何一个元素内部都会返回 true
* @param delay 延迟更新状态的时间
* @returns 返回一个数组,第一个元素是一个 ref表示鼠标是否在元素内部第二个元素是一个控制器可以通过 enable 和 disable 方法来控制监听器的启用和禁用
*/
export function useHoverToggle(
refElement: Arrayable<MaybeElementRef>,
delay: (() => number) | number = 500,
) {
const isOutsides: Array<Ref<boolean>> = [];
const value = ref(false);
const timer = ref<ReturnType<typeof setTimeout> | undefined>();
const refs = Array.isArray(refElement) ? refElement : [refElement];
refs.forEach((refEle) => {
const listener = useMouseInElement(refEle, { handleOutside: true });
isOutsides.push(listener.isOutside);
});
const isOutsideAll = computed(() => isOutsides.every((v) => v.value));
function setValueDelay(val: boolean) {
timer.value && clearTimeout(timer.value);
timer.value = setTimeout(
() => {
value.value = val;
timer.value = undefined;
},
isFunction(delay) ? delay() : delay,
);
}
const watcher = watch(
isOutsideAll,
(val) => {
setValueDelay(!val);
},
{ immediate: true },
);
const controller = {
enable() {
watcher.resume();
},
disable() {
watcher.pause();
},
};
onUnmounted(() => {
timer.value && clearTimeout(timer.value);
});
return [value, controller] as [typeof value, typeof controller];
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/layouts",
"version": "5.5.0",
"version": "5.5.1",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {

View File

@@ -103,6 +103,7 @@ const {
const {
handleMenuSelect,
handleMenuOpen,
headerActive,
headerMenus,
sidebarActive,
@@ -260,6 +261,7 @@ const headerSlots = computed(() => {
:rounded="isMenuRounded"
:theme="sidebarTheme"
mode="vertical"
@open="handleMenuOpen"
@select="handleMenuSelect"
/>
</template>

View File

@@ -14,12 +14,17 @@ const props = withDefaults(defineProps<Props>(), {
});
const emit = defineEmits<{
open: [string, string[]];
select: [string, string?];
}>();
function handleMenuSelect(key: string) {
emit('select', key, props.mode);
}
function handleMenuOpen(key: string, path: string[]) {
emit('open', key, path);
}
</script>
<template>
@@ -32,6 +37,7 @@ function handleMenuSelect(key: string) {
:mode="mode"
:rounded="rounded"
:theme="theme"
@open="handleMenuOpen"
@select="handleMenuSelect"
/>
</template>

View File

@@ -15,6 +15,9 @@ function useExtraMenu() {
const menus = computed(() => accessStore.accessMenus);
/** 记录当前顶级菜单下哪个子菜单最后激活 */
const defaultSubMap = new Map<string, string>();
const route = useRoute();
const extraMenus = ref<MenuRecordRaw[]>([]);
const sidebarExtraVisible = ref<boolean>(false);
@@ -32,6 +35,12 @@ function useExtraMenu() {
sidebarExtraVisible.value = hasChildren;
if (!hasChildren) {
await navigation(menu.path);
} else if (preferences.sidebar.autoActivateChild) {
await navigation(
defaultSubMap.has(menu.path)
? (defaultSubMap.get(menu.path) as string)
: menu.path,
);
}
};
@@ -89,6 +98,7 @@ function useExtraMenu() {
menus.value,
currentPath,
);
if (rootMenuPath) defaultSubMap.set(rootMenuPath, currentPath);
extraActiveMenu.value = rootMenuPath ?? findMenu?.path ?? '';
extraMenus.value = rootMenu?.children ?? [];
},

View File

@@ -15,7 +15,8 @@ function useMixedMenu() {
const route = useRoute();
const splitSideMenus = ref<MenuRecordRaw[]>([]);
const rootMenuPath = ref<string>('');
/** 记录当前顶级菜单下哪个子菜单最后激活 */
const defaultSubMap = new Map<string, string>();
const { isMixedNav } = usePreferences();
const needSplit = computed(
@@ -86,6 +87,25 @@ function useMixedMenu() {
splitSideMenus.value = rootMenu?.children ?? [];
if (splitSideMenus.value.length === 0) {
navigation(key);
} else if (rootMenu && preferences.sidebar.autoActivateChild) {
navigation(
defaultSubMap.has(rootMenu.path)
? (defaultSubMap.get(rootMenu.path) as string)
: rootMenu.path,
);
}
};
/**
* 侧边菜单展开事件
* @param key 路由路径
* @param parentsPath 父级路径
*/
const handleMenuOpen = (key: string, parentsPath: string[]) => {
if (parentsPath.length <= 1 && preferences.sidebar.autoActivateChild) {
navigation(
defaultSubMap.has(key) ? (defaultSubMap.get(key) as string) : key,
);
}
};
@@ -107,6 +127,8 @@ function useMixedMenu() {
(path) => {
const currentPath = (route?.meta?.activePath as string) ?? path;
calcSideMenus(currentPath);
if (rootMenuPath.value)
defaultSubMap.set(rootMenuPath.value, currentPath);
},
{ immediate: true },
);
@@ -118,6 +140,7 @@ function useMixedMenu() {
return {
handleMenuSelect,
handleMenuOpen,
headerActive,
headerMenus,
sidebarActive,

View File

@@ -55,6 +55,7 @@ if (!preferences.tabbar.persist) {
:show-icon="showIcon"
:style-type="preferences.tabbar.styleType"
:tabs="currentTabs"
:wheelable="preferences.tabbar.wheelable"
@close="handleClose"
@sort-tabs="tabbarStore.sortTabs"
@unpin="unpinTab"

View File

@@ -1,17 +1,23 @@
<script setup lang="ts">
import type { LayoutType } from '@vben/types';
import { $t } from '@vben/locales';
import NumberFieldItem from '../number-field-item.vue';
import SwitchItem from '../switch-item.vue';
defineProps<{ disabled: boolean }>();
defineProps<{ currentLayout?: LayoutType; disabled: boolean }>();
const sidebarEnable = defineModel<boolean>('sidebarEnable');
const sidebarWidth = defineModel<number>('sidebarWidth');
const sidebarCollapsedShowTitle = defineModel<boolean>(
'sidebarCollapsedShowTitle',
);
const sidebarAutoActivateChild = defineModel<boolean>(
'sidebarAutoActivateChild',
);
const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
const sidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover');
</script>
<template>
@@ -21,12 +27,32 @@ const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
<SwitchItem v-model="sidebarCollapsed" :disabled="!sidebarEnable || disabled">
{{ $t('preferences.sidebar.collapsed') }}
</SwitchItem>
<SwitchItem
v-model="sidebarExpandOnHover"
:disabled="!sidebarEnable || disabled || !sidebarCollapsed"
:tip="$t('preferences.sidebar.expandOnHoverTip')"
>
{{ $t('preferences.sidebar.expandOnHover') }}
</SwitchItem>
<SwitchItem
v-model="sidebarCollapsedShowTitle"
:disabled="!sidebarEnable || disabled || !sidebarCollapsed"
>
{{ $t('preferences.sidebar.collapsedShowTitle') }}
</SwitchItem>
<SwitchItem
v-model="sidebarAutoActivateChild"
:disabled="
!sidebarEnable ||
!['sidebar-mixed-nav', 'mixed-nav', 'sidebar-nav'].includes(
currentLayout as string,
) ||
disabled
"
:tip="$t('preferences.sidebar.autoActivateChildTip')"
>
{{ $t('preferences.sidebar.autoActivateChild') }}
</SwitchItem>
<NumberFieldItem
v-model="sidebarWidth"
:disabled="!sidebarEnable || disabled"

View File

@@ -18,6 +18,7 @@ const tabbarEnable = defineModel<boolean>('tabbarEnable');
const tabbarShowIcon = defineModel<boolean>('tabbarShowIcon');
const tabbarPersist = defineModel<boolean>('tabbarPersist');
const tabbarDraggable = defineModel<boolean>('tabbarDraggable');
const tabbarWheelable = defineModel<boolean>('tabbarWheelable');
const tabbarStyleType = defineModel<string>('tabbarStyleType');
const tabbarShowMore = defineModel<boolean>('tabbarShowMore');
const tabbarShowMaximize = defineModel<boolean>('tabbarShowMaximize');
@@ -53,6 +54,13 @@ const styleItems = computed((): SelectOption[] => [
<SwitchItem v-model="tabbarDraggable" :disabled="!tabbarEnable">
{{ $t('preferences.tabbar.draggable') }}
</SwitchItem>
<SwitchItem
v-model="tabbarWheelable"
:disabled="!tabbarEnable"
:tip="$t('preferences.tabbar.wheelableTip')"
>
{{ $t('preferences.tabbar.wheelable') }}
</SwitchItem>
<SwitchItem v-model="tabbarShowIcon" :disabled="!tabbarEnable">
{{ $t('preferences.tabbar.icon') }}
</SwitchItem>

View File

@@ -8,8 +8,9 @@ defineOptions({
name: 'PreferenceSwitchItem',
});
withDefaults(defineProps<{ disabled?: boolean }>(), {
withDefaults(defineProps<{ disabled?: boolean; tip?: string }>(), {
disabled: false,
tip: '',
});
const checked = defineModel<boolean>();
@@ -32,11 +33,17 @@ function handleClick() {
<span class="flex items-center text-sm">
<slot></slot>
<VbenTooltip v-if="slots.tip" side="bottom">
<VbenTooltip v-if="slots.tip || tip" side="bottom">
<template #trigger>
<CircleHelp class="ml-1 size-3 cursor-help" />
</template>
<slot name="tip"></slot>
<slot name="tip">
<template v-if="tip">
<p v-for="(line, index) in tip.split('\n')" :key="index">
{{ line }}
</p>
</template>
</slot>
</VbenTooltip>
</span>
<span v-if="$slots.shortcut" class="ml-auto mr-2 text-xs opacity-60">

View File

@@ -87,6 +87,10 @@ const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
const sidebarCollapsedShowTitle = defineModel<boolean>(
'sidebarCollapsedShowTitle',
);
const sidebarAutoActivateChild = defineModel<boolean>(
'sidebarAutoActivateChild',
);
const SidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover');
const headerEnable = defineModel<boolean>('headerEnable');
const headerMode = defineModel<LayoutHeaderModeType>('headerMode');
@@ -105,6 +109,7 @@ const tabbarShowMore = defineModel<boolean>('tabbarShowMore');
const tabbarShowMaximize = defineModel<boolean>('tabbarShowMaximize');
const tabbarPersist = defineModel<boolean>('tabbarPersist');
const tabbarDraggable = defineModel<boolean>('tabbarDraggable');
const tabbarWheelable = defineModel<boolean>('tabbarWheelable');
const tabbarStyleType = defineModel<string>('tabbarStyleType');
const navigationStyleType = defineModel<NavigationStyleType>(
@@ -298,10 +303,13 @@ async function handleReset() {
<Block :title="$t('preferences.sidebar.title')">
<Sidebar
v-model:sidebar-auto-activate-child="sidebarAutoActivateChild"
v-model:sidebar-collapsed="sidebarCollapsed"
v-model:sidebar-collapsed-show-title="sidebarCollapsedShowTitle"
v-model:sidebar-enable="sidebarEnable"
v-model:sidebar-expand-on-hover="SidebarExpandOnHover"
v-model:sidebar-width="sidebarWidth"
:current-layout="appLayout"
:disabled="!isSideMode"
/>
</Block>
@@ -345,6 +353,7 @@ async function handleReset() {
v-model:tabbar-show-maximize="tabbarShowMaximize"
v-model:tabbar-show-more="tabbarShowMore"
v-model:tabbar-style-type="tabbarStyleType"
v-model:tabbar-wheelable="tabbarWheelable"
/>
</Block>
<Block :title="$t('preferences.widget.title')">

View File

@@ -2,8 +2,9 @@
import type { AnyFunction } from '@vben/types';
import type { Component } from 'vue';
import { computed, ref } from 'vue';
import { computed, useTemplateRef, watch } from 'vue';
import { useHoverToggle } from '@vben/hooks';
import { LockKeyhole, LogOut } from '@vben/icons';
import { $t } from '@vben/locales';
import { preferences, usePreferences } from '@vben/preferences';
@@ -53,6 +54,10 @@ interface Props {
* 文本
*/
text?: string;
/** 触发方式 */
trigger?: 'both' | 'click' | 'hover';
/** hover触发时延迟响应的时间 */
hoverDelay?: number;
}
defineOptions({
@@ -67,10 +72,11 @@ const props = withDefaults(defineProps<Props>(), {
showShortcutKey: true,
tagText: '',
text: '',
trigger: 'click',
hoverDelay: 500,
});
const emit = defineEmits<{ logout: [] }>();
const openPopover = ref(false);
const { globalLockScreenShortcutKey, globalLogoutShortcutKey } =
usePreferences();
@@ -84,6 +90,27 @@ const [LogoutModal, logoutModalApi] = useVbenModal({
},
});
const refTrigger = useTemplateRef('refTrigger');
const refContent = useTemplateRef('refContent');
const [openPopover, hoverWatcher] = useHoverToggle(
[refTrigger, refContent],
() => props.hoverDelay,
);
watch(
() => props.trigger === 'hover' || props.trigger === 'both',
(val) => {
if (val) {
hoverWatcher.enable();
} else {
hoverWatcher.disable();
}
},
{
immediate: true,
},
);
const altView = computed(() => (isWindowsOs() ? 'Alt' : '⌥'));
const enableLogoutShortcutKey = computed(() => {
@@ -155,8 +182,8 @@ if (enableShortcutKey.value) {
{{ $t('ui.widgets.logoutTip') }}
</LogoutModal>
<DropdownMenu>
<DropdownMenuTrigger>
<DropdownMenu v-model:open="openPopover">
<DropdownMenuTrigger ref="refTrigger" :disabled="props.trigger === 'hover'">
<div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full p-1.5">
<div class="hover:text-accent-foreground flex-center">
<VbenAvatar :alt="text" :src="avatar" class="size-8" dot />
@@ -164,64 +191,66 @@ if (enableShortcutKey.value) {
</div>
</DropdownMenuTrigger>
<DropdownMenuContent class="mr-2 min-w-[240px] p-0 pb-1">
<DropdownMenuLabel class="flex items-center p-3">
<VbenAvatar
:alt="text"
:src="avatar"
class="size-12"
dot
dot-class="bottom-0 right-1 border-2 size-4 bg-green-500"
/>
<div class="ml-2 w-full">
<div
v-if="tagText || text || $slots.tagText"
class="text-foreground mb-1 flex items-center text-sm font-medium"
>
{{ text }}
<slot name="tagText">
<Badge v-if="tagText" class="ml-2 text-green-400">
{{ tagText }}
</Badge>
</slot>
<div ref="refContent">
<DropdownMenuLabel class="flex items-center p-3">
<VbenAvatar
:alt="text"
:src="avatar"
class="size-12"
dot
dot-class="bottom-0 right-1 border-2 size-4 bg-green-500"
/>
<div class="ml-2 w-full">
<div
v-if="tagText || text || $slots.tagText"
class="text-foreground mb-1 flex items-center text-sm font-medium"
>
{{ text }}
<slot name="tagText">
<Badge v-if="tagText" class="ml-2 text-green-400">
{{ tagText }}
</Badge>
</slot>
</div>
<div class="text-muted-foreground text-xs font-normal">
{{ description }}
</div>
</div>
<div class="text-muted-foreground text-xs font-normal">
{{ description }}
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator v-if="menus?.length" />
<DropdownMenuItem
v-for="menu in menus"
:key="menu.text"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
@click="menu.handler"
>
<VbenIcon :icon="menu.icon" class="mr-2 size-4" />
{{ menu.text }}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
v-if="preferences.widget.lockScreen"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
@click="handleOpenLock"
>
<LockKeyhole class="mr-2 size-4" />
{{ $t('ui.widgets.lockScreen.title') }}
<DropdownMenuShortcut v-if="enableLockScreenShortcutKey">
{{ altView }} L
</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuSeparator v-if="preferences.widget.lockScreen" />
<DropdownMenuItem
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
@click="handleLogout"
>
<LogOut class="mr-2 size-4" />
{{ $t('common.logout') }}
<DropdownMenuShortcut v-if="enableLogoutShortcutKey">
{{ altView }} Q
</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuLabel>
<DropdownMenuSeparator v-if="menus?.length" />
<DropdownMenuItem
v-for="menu in menus"
:key="menu.text"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
@click="menu.handler"
>
<VbenIcon :icon="menu.icon" class="mr-2 size-4" />
{{ menu.text }}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
v-if="preferences.widget.lockScreen"
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
@click="handleOpenLock"
>
<LockKeyhole class="mr-2 size-4" />
{{ $t('ui.widgets.lockScreen.title') }}
<DropdownMenuShortcut v-if="enableLockScreenShortcutKey">
{{ altView }} L
</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuSeparator v-if="preferences.widget.lockScreen" />
<DropdownMenuItem
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
@click="handleLogout"
>
<LogOut class="mr-2 size-4" />
{{ $t('common.logout') }}
<DropdownMenuShortcut v-if="enableLogoutShortcutKey">
{{ altView }} Q
</DropdownMenuShortcut>
</DropdownMenuItem>
</div>
</DropdownMenuContent>
</DropdownMenu>
</template>

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/plugins",
"version": "5.5.0",
"version": "5.5.1",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {

View File

@@ -8,6 +8,7 @@ import { toRaw } from 'vue';
import { Store } from '@vben-core/shared/store';
import {
bindMethods,
isBoolean,
isFunction,
mergeWithArrayOverride,
StateHandler,
@@ -20,6 +21,7 @@ function getDefaultState(): VxeGridProps {
gridOptions: {},
gridEvents: {},
formOptions: undefined,
showSearchForm: true,
};
}
@@ -108,6 +110,16 @@ export class VxeGridApi {
}
}
toggleSearchForm(show?: boolean) {
this.setState({
showSearchForm: isBoolean(show) ? show : !this.state?.showSearchForm,
});
// nextTick(() => {
// this.grid.recalculate();
// });
return this.state?.showSearchForm;
}
unmount() {
this.isMounted = false;
this.stateHandler.reset();

View File

@@ -1,4 +1,5 @@
export { setupVbenVxeTable } from './init';
export type { VxeTableGridOptions } from './types';
export * from './use-vxe-grid';
export { default as VbenVxeGrid } from './use-vxe-grid.vue';
export type { VxeGridDefines, VxeGridListeners, VxeGridProps } from 'vxe-table';

View File

@@ -2,6 +2,7 @@ import type { ClassType, DeepPartial } from '@vben/types';
import type { VbenFormProps } from '@vben-core/form-ui';
import type {
VxeGridListeners,
VxeGridPropTypes,
VxeGridProps as VxeTableGridProps,
VxeUIExport,
} from 'vxe-table';
@@ -18,6 +19,16 @@ export interface VxePaginationInfo {
total: number;
}
interface ToolbarConfigOptions extends VxeGridPropTypes.ToolbarConfig {
/** 是否显示切换搜索表单的按钮 */
search?: boolean;
}
export interface VxeTableGridOptions<T = any> extends VxeTableGridProps<T> {
/** 工具栏配置 */
toolbarConfig?: ToolbarConfigOptions;
}
export interface VxeGridProps {
/**
* 标题
@@ -38,7 +49,7 @@ export interface VxeGridProps {
/**
* vxe-grid 配置
*/
gridOptions?: DeepPartial<VxeTableGridProps>;
gridOptions?: DeepPartial<VxeTableGridOptions>;
/**
* vxe-grid 事件
*/
@@ -47,6 +58,10 @@ export interface VxeGridProps {
* 表单配置
*/
formOptions?: VbenFormProps;
/**
* 显示搜索表单
*/
showSearchForm?: boolean;
}
export type ExtendedVxeGridApi = {

View File

@@ -1,7 +1,10 @@
<script lang="ts" setup>
import type { VbenFormProps } from '@vben-core/form-ui';
import type {
VxeGridDefines,
VxeGridInstance,
VxeGridListeners,
VxeGridPropTypes,
VxeGridProps as VxeTableGridProps,
} from 'vxe-table';
@@ -57,6 +60,7 @@ const {
formOptions,
tableTitle,
tableTitleHelp,
showSearchForm,
} = usePriorityValues(props, state);
const { isMobile } = usePreferences();
@@ -105,21 +109,37 @@ const toolbarOptions = computed(() => {
const slotActions = slots[TOOLBAR_ACTIONS]?.();
const slotTools = slots[TOOLBAR_TOOLS]?.();
const toolbarConfig: VxeGridPropTypes.ToolbarConfig = {
tools:
gridOptions.value?.toolbarConfig?.search && !!formOptions.value
? [
{
code: 'search',
icon: 'vxe-icon--search',
circle: true,
status: showSearchForm.value ? 'primary' : undefined,
title: $t('common.search'),
},
]
: [],
};
if (!showToolbar.value) {
return {};
return { toolbarConfig };
}
// if (gridOptions.value?.toolbarConfig?.search) {
// }
// 强制使用固定的toolbar配置不允许用户自定义
// 减少配置的复杂度,以及后续维护的成本
return {
toolbarConfig: {
slots: {
...(slotActions || showTableTitle.value
? { buttons: TOOLBAR_ACTIONS }
: {}),
...(slotTools ? { tools: TOOLBAR_TOOLS } : {}),
},
},
toolbarConfig.slots = {
...(slotActions || showTableTitle.value
? { buttons: TOOLBAR_ACTIONS }
: {}),
...(slotTools ? { tools: TOOLBAR_TOOLS } : {}),
};
return { toolbarConfig };
});
const options = computed(() => {
@@ -175,9 +195,19 @@ const options = computed(() => {
return mergedOptions;
});
function onToolbarToolClick(event: VxeGridDefines.ToolbarToolClickEventParams) {
if (event.code === 'search') {
props.api?.toggleSearchForm?.();
}
(
gridEvents.value?.toolbarToolClick as VxeGridListeners['toolbarToolClick']
)?.(event);
}
const events = computed(() => {
return {
...gridEvents.value,
toolbarToolClick: onToolbarToolClick,
};
});
@@ -215,7 +245,8 @@ async function init() {
const autoLoad = defaultGridOptions.proxyConfig?.autoLoad;
const enableProxyConfig = options.value.proxyConfig?.enabled;
if (enableProxyConfig && autoLoad) {
props.api.reload(formApi.form?.values ?? {});
props.api.grid.commitProxy?.('_init', formApi.form?.values ?? {});
// props.api.reload(formApi.form?.values ?? {});
}
// form 由 vben-form代替所以不适配formConfig这里给出警告
@@ -306,7 +337,11 @@ onUnmounted(() => {
<!-- form表单 -->
<template #form>
<div v-if="formOptions" class="relative rounded py-3 pb-4">
<div
v-if="formOptions"
v-show="showSearchForm !== false"
class="relative rounded py-3 pb-4"
>
<slot name="form">
<Form>
<template

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/request",
"version": "5.5.0",
"version": "5.5.1",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {