Trim layout widgets runtime
This commit is contained in:
@@ -33,9 +33,6 @@ const AuthenticationLoginExpiredModal = defineAsyncComponent(() =>
|
|||||||
const UserDropdown = defineAsyncComponent(() =>
|
const UserDropdown = defineAsyncComponent(() =>
|
||||||
import('@vben/layouts/es/user-dropdown').then((module) => module.UserDropdown),
|
import('@vben/layouts/es/user-dropdown').then((module) => module.UserDropdown),
|
||||||
);
|
);
|
||||||
const LockScreen = defineAsyncComponent(() =>
|
|
||||||
import('@vben/layouts/es/lock-screen').then((module) => module.LockScreen),
|
|
||||||
);
|
|
||||||
const NotificationPopup = defineAsyncComponent(
|
const NotificationPopup = defineAsyncComponent(
|
||||||
() => import('#/components/notification/NotificationPopup.vue'),
|
() => import('#/components/notification/NotificationPopup.vue'),
|
||||||
);
|
);
|
||||||
@@ -275,7 +272,7 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<BasicLayout @clear-preferences-and-logout="handleLogout">
|
<BasicLayout>
|
||||||
<template #user-dropdown>
|
<template #user-dropdown>
|
||||||
<UserDropdown
|
<UserDropdown
|
||||||
:avatar
|
:avatar
|
||||||
@@ -336,8 +333,5 @@ watch(
|
|||||||
<LoginForm />
|
<LoginForm />
|
||||||
</AuthenticationLoginExpiredModal>
|
</AuthenticationLoginExpiredModal>
|
||||||
</template>
|
</template>
|
||||||
<template #lock-screen>
|
|
||||||
<LockScreen :avatar @to-login="handleLogout" />
|
|
||||||
</template>
|
|
||||||
</BasicLayout>
|
</BasicLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -12,11 +12,20 @@ export const overridesPreferences = defineOverridesPreferences({
|
|||||||
app: {
|
app: {
|
||||||
name: import.meta.env.VITE_APP_TITLE,
|
name: import.meta.env.VITE_APP_TITLE,
|
||||||
enableRefreshToken: true,
|
enableRefreshToken: true,
|
||||||
|
enableCheckUpdates: false,
|
||||||
|
enablePreferences: false,
|
||||||
accessMode: 'mixed',
|
accessMode: 'mixed',
|
||||||
authPageLayout: 'panel-center',
|
authPageLayout: 'panel-center',
|
||||||
defaultHomePath: '/control-center',
|
defaultHomePath: '/control-center',
|
||||||
layout: 'header-sidebar-nav',
|
layout: 'header-sidebar-nav',
|
||||||
},
|
},
|
||||||
|
shortcutKeys: {
|
||||||
|
enable: false,
|
||||||
|
globalLockScreen: false,
|
||||||
|
globalLogout: true,
|
||||||
|
globalPreferences: false,
|
||||||
|
globalSearch: false,
|
||||||
|
},
|
||||||
theme: {
|
theme: {
|
||||||
mode: 'light',
|
mode: 'light',
|
||||||
},
|
},
|
||||||
@@ -37,4 +46,16 @@ export const overridesPreferences = defineOverridesPreferences({
|
|||||||
fit: 'contain',
|
fit: 'contain',
|
||||||
source: Logo,
|
source: Logo,
|
||||||
},
|
},
|
||||||
|
widget: {
|
||||||
|
fullscreen: false,
|
||||||
|
globalSearch: false,
|
||||||
|
languageToggle: false,
|
||||||
|
lockScreen: false,
|
||||||
|
notification: true,
|
||||||
|
refresh: false,
|
||||||
|
sidebarBranding: true,
|
||||||
|
sidebarToggle: true,
|
||||||
|
themeToggle: true,
|
||||||
|
timezone: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -94,6 +94,23 @@ class PreferenceManager {
|
|||||||
if (overrides?.app?.enablePreferences !== undefined) {
|
if (overrides?.app?.enablePreferences !== undefined) {
|
||||||
mergedPreference.app.enablePreferences = overrides.app.enablePreferences;
|
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) {
|
if (overrides?.logo?.source) {
|
||||||
mergedPreference.logo.source = overrides.logo.source;
|
mergedPreference.logo.source = overrides.logo.source;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,10 @@ import { computed } from 'vue';
|
|||||||
|
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
|
|
||||||
import {
|
import AuthenticationColorToggle from '../widgets/color-toggle.vue';
|
||||||
AuthenticationColorToggle,
|
import LanguageToggle from '../widgets/language-toggle.vue';
|
||||||
AuthenticationLayoutToggle,
|
import AuthenticationLayoutToggle from '../widgets/layout-toggle.vue';
|
||||||
LanguageToggle,
|
import ThemeToggle from '../widgets/theme-toggle/theme-toggle.vue';
|
||||||
ThemeToggle,
|
|
||||||
} from '../widgets';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
toolbarList?: ToolbarType[];
|
toolbarList?: ToolbarType[];
|
||||||
|
|||||||
@@ -1,20 +1,27 @@
|
|||||||
<script lang="ts" setup>
|
<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 { preferences, usePreferences } from '@vben/preferences';
|
||||||
import { useAccessStore } from '@vben/stores';
|
import { useAccessStore } from '@vben/stores';
|
||||||
|
|
||||||
import { VbenFullScreen, VbenIconButton } from '@vben-core/shadcn-ui';
|
const GlobalSearch = defineAsyncComponent(
|
||||||
|
() => import('../../widgets/global-search/global-search.vue'),
|
||||||
import {
|
);
|
||||||
GlobalSearch,
|
const LanguageToggle = defineAsyncComponent(
|
||||||
LanguageToggle,
|
() => import('../../widgets/language-toggle.vue'),
|
||||||
PreferencesButton,
|
);
|
||||||
ThemeToggle,
|
const ThemeToggle = defineAsyncComponent(
|
||||||
TimezoneButton,
|
() => import('../../widgets/theme-toggle/theme-toggle.vue'),
|
||||||
} from '../../widgets';
|
);
|
||||||
|
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 {
|
interface Props {
|
||||||
/**
|
/**
|
||||||
@@ -31,14 +38,11 @@ withDefaults(defineProps<Props>(), {
|
|||||||
theme: 'light',
|
theme: 'light',
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{ clearPreferencesAndLogout: [] }>();
|
|
||||||
|
|
||||||
const REFERENCE_VALUE = 50;
|
const REFERENCE_VALUE = 50;
|
||||||
|
|
||||||
const accessStore = useAccessStore();
|
const accessStore = useAccessStore();
|
||||||
const { globalSearchShortcutKey, preferencesButtonPosition } = usePreferences();
|
const { globalSearchShortcutKey } = usePreferences();
|
||||||
const slots = useSlots();
|
const slots = useSlots();
|
||||||
const { refresh } = useRefresh();
|
|
||||||
|
|
||||||
const rightSlots = computed(() => {
|
const rightSlots = computed(() => {
|
||||||
const list = [{ index: REFERENCE_VALUE + 100, name: 'user-dropdown' }];
|
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) {
|
if (preferences.widget.themeToggle) {
|
||||||
list.push({
|
list.push({
|
||||||
index: REFERENCE_VALUE + 20,
|
index: REFERENCE_VALUE + 20,
|
||||||
@@ -98,13 +96,6 @@ const rightSlots = computed(() => {
|
|||||||
const leftSlots = computed(() => {
|
const leftSlots = computed(() => {
|
||||||
const list: Array<{ index: number; name: string }> = [];
|
const list: Array<{ index: number; name: string }> = [];
|
||||||
|
|
||||||
if (preferences.widget.refresh) {
|
|
||||||
list.push({
|
|
||||||
index: 0,
|
|
||||||
name: 'refresh',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.keys(slots).forEach((key) => {
|
Object.keys(slots).forEach((key) => {
|
||||||
const name = key.split('-');
|
const name = key.split('-');
|
||||||
if (key.startsWith('header-left')) {
|
if (key.startsWith('header-left')) {
|
||||||
@@ -114,9 +105,6 @@ const leftSlots = computed(() => {
|
|||||||
return list.sort((a, b) => a.index - b.index);
|
return list.sort((a, b) => a.index - b.index);
|
||||||
});
|
});
|
||||||
|
|
||||||
function clearPreferencesAndLogout() {
|
|
||||||
emit('clearPreferencesAndLogout');
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -125,11 +113,6 @@ function clearPreferencesAndLogout() {
|
|||||||
:key="slot.name"
|
:key="slot.name"
|
||||||
>
|
>
|
||||||
<slot :name="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>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
<div class="flex-center hidden lg:block">
|
<div class="flex-center hidden lg:block">
|
||||||
@@ -158,12 +141,6 @@ function clearPreferencesAndLogout() {
|
|||||||
/>
|
/>
|
||||||
</template>
|
</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'">
|
<template v-else-if="slot.name === 'theme-toggle'">
|
||||||
<ThemeToggle class="mr-1 mt-[2px]" />
|
<ThemeToggle class="mr-1 mt-[2px]" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -14,13 +14,12 @@ import {
|
|||||||
updatePreferences,
|
updatePreferences,
|
||||||
usePreferences,
|
usePreferences,
|
||||||
} from '@vben/preferences';
|
} from '@vben/preferences';
|
||||||
import { useAccessStore } from '@vben/stores';
|
|
||||||
import { cloneDeep, mapTree } from '@vben/utils';
|
import { cloneDeep, mapTree } from '@vben/utils';
|
||||||
|
|
||||||
import { VbenAdminLayout } from '@vben-core/layout-ui';
|
import { VbenAdminLayout } from '@vben-core/layout-ui';
|
||||||
import { VbenBackTop, VbenLogo } from '@vben-core/shadcn-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 { LayoutContent, LayoutContentSpinner } from './content';
|
||||||
import { Copyright } from './copyright';
|
import { Copyright } from './copyright';
|
||||||
import { LayoutFooter } from './footer';
|
import { LayoutFooter } from './footer';
|
||||||
@@ -36,7 +35,7 @@ import { LayoutTabbar } from './tabbar';
|
|||||||
|
|
||||||
defineOptions({ name: 'BasicLayout' });
|
defineOptions({ name: 'BasicLayout' });
|
||||||
|
|
||||||
const emit = defineEmits<{ clearPreferencesAndLogout: []; clickLogo: [] }>();
|
const emit = defineEmits<{ clickLogo: [] }>();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isDark,
|
isDark,
|
||||||
@@ -47,11 +46,9 @@ const {
|
|||||||
isHeaderMixedNav,
|
isHeaderMixedNav,
|
||||||
isHeaderSidebarNav,
|
isHeaderSidebarNav,
|
||||||
layout,
|
layout,
|
||||||
preferencesButtonPosition,
|
|
||||||
sidebarCollapsed,
|
sidebarCollapsed,
|
||||||
theme,
|
theme,
|
||||||
} = usePreferences();
|
} = usePreferences();
|
||||||
const accessStore = useAccessStore();
|
|
||||||
const { refresh } = useRefresh();
|
const { refresh } = useRefresh();
|
||||||
|
|
||||||
const sidebarTheme = computed(() => {
|
const sidebarTheme = computed(() => {
|
||||||
@@ -147,10 +144,6 @@ function toggleSidebar() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearPreferencesAndLogout() {
|
|
||||||
emit('clearPreferencesAndLogout');
|
|
||||||
}
|
|
||||||
|
|
||||||
function clickLogo() {
|
function clickLogo() {
|
||||||
emit('clickLogo');
|
emit('clickLogo');
|
||||||
}
|
}
|
||||||
@@ -271,10 +264,7 @@ const headerSlots = computed(() => {
|
|||||||
</template>
|
</template>
|
||||||
<!-- 头部区域 -->
|
<!-- 头部区域 -->
|
||||||
<template #header>
|
<template #header>
|
||||||
<LayoutHeader
|
<LayoutHeader :theme="theme">
|
||||||
:theme="theme"
|
|
||||||
@clear-preferences-and-logout="clearPreferencesAndLogout"
|
|
||||||
>
|
|
||||||
<template
|
<template
|
||||||
v-if="!showHeaderNav && preferences.breadcrumb.enable"
|
v-if="!showHeaderNav && preferences.breadcrumb.enable"
|
||||||
#breadcrumb
|
#breadcrumb
|
||||||
@@ -392,21 +382,6 @@ const headerSlots = computed(() => {
|
|||||||
|
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<slot name="extra"></slot>
|
<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 />
|
<VbenBackTop />
|
||||||
</template>
|
</template>
|
||||||
</VbenAdminLayout>
|
</VbenAdminLayout>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
export * from './authentication';
|
export * from './authentication';
|
||||||
export * from './basic';
|
export * from './basic';
|
||||||
export * from './iframe';
|
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 { type CSSProperties, computed, useTemplateRef, watch } from 'vue';
|
||||||
|
|
||||||
import { useHoverToggle } from '@vben/hooks';
|
import { useHoverToggle } from '@vben/hooks';
|
||||||
import { LockKeyhole, LogOut } from '@vben/icons';
|
import { LogOut } from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
import { preferences, usePreferences } from '@vben/preferences';
|
import { preferences, usePreferences } from '@vben/preferences';
|
||||||
import { useAccessStore } from '@vben/stores';
|
|
||||||
import { isWindowsOs } from '@vben/utils';
|
import { isWindowsOs } from '@vben/utils';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben-core/popup-ui';
|
import { useVbenModal } from '@vben-core/popup-ui';
|
||||||
@@ -28,8 +27,6 @@ import {
|
|||||||
|
|
||||||
import { useMagicKeys, whenever } from '@vueuse/core';
|
import { useMagicKeys, whenever } from '@vueuse/core';
|
||||||
|
|
||||||
import { LockScreenModal } from '../lock-screen';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/**
|
/**
|
||||||
* 头像
|
* 头像
|
||||||
@@ -148,12 +145,7 @@ const fallbackAvatarStyle = computed<CSSProperties>(() => ({
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { globalLockScreenShortcutKey, globalLogoutShortcutKey } =
|
const { globalLogoutShortcutKey } = usePreferences();
|
||||||
usePreferences();
|
|
||||||
const accessStore = useAccessStore();
|
|
||||||
const [LockModal, lockModalApi] = useVbenModal({
|
|
||||||
connectedComponent: LockScreenModal,
|
|
||||||
});
|
|
||||||
const [LogoutModal, logoutModalApi] = useVbenModal({
|
const [LogoutModal, logoutModalApi] = useVbenModal({
|
||||||
onConfirm() {
|
onConfirm() {
|
||||||
handleSubmitLogout();
|
handleSubmitLogout();
|
||||||
@@ -187,23 +179,10 @@ const enableLogoutShortcutKey = computed(() => {
|
|||||||
return props.enableShortcutKey && globalLogoutShortcutKey.value;
|
return props.enableShortcutKey && globalLogoutShortcutKey.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
const enableLockScreenShortcutKey = computed(() => {
|
|
||||||
return props.enableShortcutKey && globalLockScreenShortcutKey.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
const enableShortcutKey = computed(() => {
|
const enableShortcutKey = computed(() => {
|
||||||
return props.enableShortcutKey && preferences.shortcutKeys.enable;
|
return props.enableShortcutKey && preferences.shortcutKeys.enable;
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleOpenLock() {
|
|
||||||
lockModalApi.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSubmitLock(lockScreenPassword: string) {
|
|
||||||
lockModalApi.close();
|
|
||||||
accessStore.lockScreen(lockScreenPassword);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleLogout() {
|
function handleLogout() {
|
||||||
// emit
|
// emit
|
||||||
logoutModalApi.open();
|
logoutModalApi.open();
|
||||||
@@ -222,23 +201,10 @@ if (enableShortcutKey.value) {
|
|||||||
handleLogout();
|
handleLogout();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
whenever(keys['Alt+KeyL']!, () => {
|
|
||||||
if (enableLockScreenShortcutKey.value) {
|
|
||||||
handleOpenLock();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<LockModal
|
|
||||||
v-if="preferences.widget.lockScreen"
|
|
||||||
:avatar="avatar"
|
|
||||||
:text="text"
|
|
||||||
@submit="handleSubmitLock"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<LogoutModal
|
<LogoutModal
|
||||||
:cancel-text="$t('common.cancel')"
|
:cancel-text="$t('common.cancel')"
|
||||||
:confirm-text="$t('common.confirm')"
|
:confirm-text="$t('common.confirm')"
|
||||||
@@ -317,18 +283,6 @@ if (enableShortcutKey.value) {
|
|||||||
{{ menu.text }}
|
{{ menu.text }}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<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
|
<DropdownMenuItem
|
||||||
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
|
||||||
@click="handleLogout"
|
@click="handleLogout"
|
||||||
|
|||||||
Reference in New Issue
Block a user