Trim layout widgets runtime

This commit is contained in:
2026-06-09 18:01:50 +08:00
parent 31389dcfd2
commit 75f7cacc17
8 changed files with 68 additions and 133 deletions
@@ -94,6 +94,23 @@ class PreferenceManager {
if (overrides?.app?.enablePreferences !== undefined) {
mergedPreference.app.enablePreferences = overrides.app.enablePreferences;
}
if (overrides?.app?.enableCheckUpdates !== undefined) {
mergedPreference.app.enableCheckUpdates = overrides.app.enableCheckUpdates;
}
if (overrides?.shortcutKeys) {
mergedPreference.shortcutKeys = merge(
{},
overrides.shortcutKeys,
mergedPreference.shortcutKeys,
);
}
if (overrides?.widget) {
mergedPreference.widget = merge(
{},
overrides.widget,
mergedPreference.widget,
);
}
if (overrides?.logo?.source) {
mergedPreference.logo.source = overrides.logo.source;
}
@@ -5,12 +5,10 @@ import { computed } from 'vue';
import { preferences } from '@vben/preferences';
import {
AuthenticationColorToggle,
AuthenticationLayoutToggle,
LanguageToggle,
ThemeToggle,
} from '../widgets';
import AuthenticationColorToggle from '../widgets/color-toggle.vue';
import LanguageToggle from '../widgets/language-toggle.vue';
import AuthenticationLayoutToggle from '../widgets/layout-toggle.vue';
import ThemeToggle from '../widgets/theme-toggle/theme-toggle.vue';
interface Props {
toolbarList?: ToolbarType[];
@@ -1,20 +1,27 @@
<script lang="ts" setup>
import { computed, useSlots } from 'vue';
import { computed, defineAsyncComponent, useSlots } from 'vue';
import { useRefresh } from '@vben/hooks';
import { RotateCw } from '@vben/icons';
import { preferences, usePreferences } from '@vben/preferences';
import { useAccessStore } from '@vben/stores';
import { VbenFullScreen, VbenIconButton } from '@vben-core/shadcn-ui';
import {
GlobalSearch,
LanguageToggle,
PreferencesButton,
ThemeToggle,
TimezoneButton,
} from '../../widgets';
const GlobalSearch = defineAsyncComponent(
() => import('../../widgets/global-search/global-search.vue'),
);
const LanguageToggle = defineAsyncComponent(
() => import('../../widgets/language-toggle.vue'),
);
const ThemeToggle = defineAsyncComponent(
() => import('../../widgets/theme-toggle/theme-toggle.vue'),
);
const TimezoneButton = defineAsyncComponent(
() => import('../../widgets/timezone/timezone-button.vue'),
);
const VbenFullScreen = defineAsyncComponent(
() =>
import(
'../../../../../@core/ui-kit/shadcn-ui/src/components/full-screen/full-screen.vue'
),
);
interface Props {
/**
@@ -31,14 +38,11 @@ withDefaults(defineProps<Props>(), {
theme: 'light',
});
const emit = defineEmits<{ clearPreferencesAndLogout: [] }>();
const REFERENCE_VALUE = 50;
const accessStore = useAccessStore();
const { globalSearchShortcutKey, preferencesButtonPosition } = usePreferences();
const { globalSearchShortcutKey } = usePreferences();
const slots = useSlots();
const { refresh } = useRefresh();
const rightSlots = computed(() => {
const list = [{ index: REFERENCE_VALUE + 100, name: 'user-dropdown' }];
@@ -49,12 +53,6 @@ const rightSlots = computed(() => {
});
}
if (preferencesButtonPosition.value.header) {
list.push({
index: REFERENCE_VALUE + 10,
name: 'preferences',
});
}
if (preferences.widget.themeToggle) {
list.push({
index: REFERENCE_VALUE + 20,
@@ -98,13 +96,6 @@ const rightSlots = computed(() => {
const leftSlots = computed(() => {
const list: Array<{ index: number; name: string }> = [];
if (preferences.widget.refresh) {
list.push({
index: 0,
name: 'refresh',
});
}
Object.keys(slots).forEach((key) => {
const name = key.split('-');
if (key.startsWith('header-left')) {
@@ -114,9 +105,6 @@ const leftSlots = computed(() => {
return list.sort((a, b) => a.index - b.index);
});
function clearPreferencesAndLogout() {
emit('clearPreferencesAndLogout');
}
</script>
<template>
@@ -125,11 +113,6 @@ function clearPreferencesAndLogout() {
:key="slot.name"
>
<slot :name="slot.name">
<template v-if="slot.name === 'refresh'">
<VbenIconButton class="my-0 mr-1 rounded-md" @click="refresh">
<RotateCw class="size-4" />
</VbenIconButton>
</template>
</slot>
</template>
<div class="flex-center hidden lg:block">
@@ -158,12 +141,6 @@ function clearPreferencesAndLogout() {
/>
</template>
<template v-else-if="slot.name === 'preferences'">
<PreferencesButton
class="mr-1"
@clear-preferences-and-logout="clearPreferencesAndLogout"
/>
</template>
<template v-else-if="slot.name === 'theme-toggle'">
<ThemeToggle class="mr-1 mt-[2px]" />
</template>
@@ -14,13 +14,12 @@ import {
updatePreferences,
usePreferences,
} from '@vben/preferences';
import { useAccessStore } from '@vben/stores';
import { cloneDeep, mapTree } from '@vben/utils';
import { VbenAdminLayout } from '@vben-core/layout-ui';
import { VbenBackTop, VbenLogo } from '@vben-core/shadcn-ui';
import { Breadcrumb, CheckUpdates, Preferences } from '../widgets';
import Breadcrumb from '../widgets/breadcrumb.vue';
import { LayoutContent, LayoutContentSpinner } from './content';
import { Copyright } from './copyright';
import { LayoutFooter } from './footer';
@@ -36,7 +35,7 @@ import { LayoutTabbar } from './tabbar';
defineOptions({ name: 'BasicLayout' });
const emit = defineEmits<{ clearPreferencesAndLogout: []; clickLogo: [] }>();
const emit = defineEmits<{ clickLogo: [] }>();
const {
isDark,
@@ -47,11 +46,9 @@ const {
isHeaderMixedNav,
isHeaderSidebarNav,
layout,
preferencesButtonPosition,
sidebarCollapsed,
theme,
} = usePreferences();
const accessStore = useAccessStore();
const { refresh } = useRefresh();
const sidebarTheme = computed(() => {
@@ -147,10 +144,6 @@ function toggleSidebar() {
});
}
function clearPreferencesAndLogout() {
emit('clearPreferencesAndLogout');
}
function clickLogo() {
emit('clickLogo');
}
@@ -271,10 +264,7 @@ const headerSlots = computed(() => {
</template>
<!-- 头部区域 -->
<template #header>
<LayoutHeader
:theme="theme"
@clear-preferences-and-logout="clearPreferencesAndLogout"
>
<LayoutHeader :theme="theme">
<template
v-if="!showHeaderNav && preferences.breadcrumb.enable"
#breadcrumb
@@ -392,21 +382,6 @@ const headerSlots = computed(() => {
<template #extra>
<slot name="extra"></slot>
<CheckUpdates
v-if="preferences.app.enableCheckUpdates"
:check-updates-interval="preferences.app.checkUpdatesInterval"
/>
<Transition v-if="preferences.widget.lockScreen" name="slide-up">
<slot v-if="accessStore.isLockScreen" name="lock-screen"></slot>
</Transition>
<template v-if="preferencesButtonPosition.fixed">
<Preferences
class="z-100 fixed bottom-20 right-0"
@clear-preferences-and-logout="clearPreferencesAndLogout"
/>
</template>
<VbenBackTop />
</template>
</VbenAdminLayout>
@@ -1,4 +1,3 @@
export * from './authentication';
export * from './basic';
export * from './iframe';
export * from './widgets';
@@ -6,10 +6,9 @@ import type { AnyFunction } from '@vben/types';
import { type CSSProperties, computed, useTemplateRef, watch } from 'vue';
import { useHoverToggle } from '@vben/hooks';
import { LockKeyhole, LogOut } from '@vben/icons';
import { LogOut } from '@vben/icons';
import { $t } from '@vben/locales';
import { preferences, usePreferences } from '@vben/preferences';
import { useAccessStore } from '@vben/stores';
import { isWindowsOs } from '@vben/utils';
import { useVbenModal } from '@vben-core/popup-ui';
@@ -28,8 +27,6 @@ import {
import { useMagicKeys, whenever } from '@vueuse/core';
import { LockScreenModal } from '../lock-screen';
interface Props {
/**
* 头像
@@ -148,12 +145,7 @@ const fallbackAvatarStyle = computed<CSSProperties>(() => ({
fontWeight: 600,
}));
const { globalLockScreenShortcutKey, globalLogoutShortcutKey } =
usePreferences();
const accessStore = useAccessStore();
const [LockModal, lockModalApi] = useVbenModal({
connectedComponent: LockScreenModal,
});
const { globalLogoutShortcutKey } = usePreferences();
const [LogoutModal, logoutModalApi] = useVbenModal({
onConfirm() {
handleSubmitLogout();
@@ -187,23 +179,10 @@ const enableLogoutShortcutKey = computed(() => {
return props.enableShortcutKey && globalLogoutShortcutKey.value;
});
const enableLockScreenShortcutKey = computed(() => {
return props.enableShortcutKey && globalLockScreenShortcutKey.value;
});
const enableShortcutKey = computed(() => {
return props.enableShortcutKey && preferences.shortcutKeys.enable;
});
function handleOpenLock() {
lockModalApi.open();
}
function handleSubmitLock(lockScreenPassword: string) {
lockModalApi.close();
accessStore.lockScreen(lockScreenPassword);
}
function handleLogout() {
// emit
logoutModalApi.open();
@@ -222,23 +201,10 @@ if (enableShortcutKey.value) {
handleLogout();
}
});
whenever(keys['Alt+KeyL']!, () => {
if (enableLockScreenShortcutKey.value) {
handleOpenLock();
}
});
}
</script>
<template>
<LockModal
v-if="preferences.widget.lockScreen"
:avatar="avatar"
:text="text"
@submit="handleSubmitLock"
/>
<LogoutModal
:cancel-text="$t('common.cancel')"
:confirm-text="$t('common.confirm')"
@@ -317,18 +283,6 @@ if (enableShortcutKey.value) {
{{ 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"