admin-vben5/packages/effects/layouts/src/widgets/preferences/preferences-sheet.vue

426 lines
14 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import type { SegmentedItem } from '@vben-core/shadcn-ui';
import type {
BreadcrumbStyleType,
BuiltinThemeType,
ContentCompactType,
LayoutHeaderModeType,
LayoutType,
NavigationStyleType,
SupportedLanguagesType,
ThemeModeType,
} from '@vben-core/typings';
import { computed, ref } from 'vue';
import { IcRoundFolderCopy, IcRoundRestartAlt } from '@vben-core/iconify';
import { $t, loadLocaleMessages } from '@vben-core/locales';
import {
clearPreferencesCache,
preferences,
resetPreferences,
usePreferences,
} from '@vben-core/preferences';
import {
VbenButton,
VbenIconButton,
VbenSegmented,
VbenSheet,
useToast,
} from '@vben-core/shadcn-ui';
import { useClipboard } from '@vueuse/core';
import {
Animation,
Block,
Breadcrumb,
BuiltinTheme,
ColorMode,
Content,
Copyright,
Footer,
General,
GlobalShortcutKeys,
Header,
Layout,
Navigation,
Radius,
Sidebar,
Tabbar,
Theme,
Widget,
} from './blocks';
import IconSetting from './icons/setting.vue';
import { useOpenPreferences } from './use-open-preferences';
const emit = defineEmits<{ clearPreferencesAndLogout: [] }>();
const { toast } = useToast();
const appLocale = defineModel<SupportedLanguagesType>('appLocale');
const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
const appLayout = defineModel<LayoutType>('appLayout');
const appColorGrayMode = defineModel<boolean>('appColorGrayMode');
const appColorWeakMode = defineModel<boolean>('appColorWeakMode');
const appSemiDarkMenu = defineModel<boolean>('appSemiDarkMenu');
const appContentCompact = defineModel<ContentCompactType>('appContentCompact');
const transitionProgress = defineModel<boolean>('transitionProgress');
const transitionName = defineModel<string>('transitionName');
const transitionLoading = defineModel<boolean>('transitionLoading');
const transitionEnable = defineModel<boolean>('transitionEnable');
const themeColorPrimary = defineModel<string>('themeColorPrimary');
const themeBuiltinType = defineModel<BuiltinThemeType>('themeBuiltinType');
const themeMode = defineModel<ThemeModeType>('themeMode');
const themeRadius = defineModel<string>('themeRadius');
const sidebarEnable = defineModel<boolean>('sidebarEnable');
const sidebarWidth = defineModel<number>('sidebarWidth');
const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
const sidebarCollapsedShowTitle = defineModel<boolean>(
'sidebarCollapsedShowTitle',
);
const headerEnable = defineModel<boolean>('headerEnable');
const headerMode = defineModel<LayoutHeaderModeType>('headerMode');
const breadcrumbEnable = defineModel<boolean>('breadcrumbEnable');
const breadcrumbShowIcon = defineModel<boolean>('breadcrumbShowIcon');
const breadcrumbShowHome = defineModel<boolean>('breadcrumbShowHome');
const breadcrumbStyleType = defineModel<BreadcrumbStyleType>(
'breadcrumbStyleType',
);
const breadcrumbHideOnlyOne = defineModel<boolean>('breadcrumbHideOnlyOne');
const tabbarEnable = defineModel<boolean>('tabbarEnable');
const tabbarShowIcon = defineModel<boolean>('tabbarShowIcon');
2024-07-10 00:50:41 +08:00
const tabbarPersist = defineModel<boolean>('tabbarPersist');
const navigationStyleType = defineModel<NavigationStyleType>(
'navigationStyleType',
);
const navigationSplit = defineModel<boolean>('navigationSplit');
const navigationAccordion = defineModel<boolean>('navigationAccordion');
// const logoVisible = defineModel<boolean>('logoVisible');
const footerEnable = defineModel<boolean>('footerEnable');
const footerFixed = defineModel<boolean>('footerFixed');
const copyrightEnable = defineModel<boolean>('copyrightEnable');
const copyrightCompanyName = defineModel<string>('copyrightCompanyName');
const copyrightCompanySiteLink = defineModel<string>(
'copyrightCompanySiteLink',
);
const copyrightDate = defineModel<string>('copyrightDate');
const copyrightIcp = defineModel<string>('copyrightIcp');
const copyrightIcpLink = defineModel<string>('copyrightIcpLink');
const shortcutKeysEnable = defineModel<boolean>('shortcutKeysEnable');
const shortcutKeysGlobalSearch = defineModel<boolean>(
'shortcutKeysGlobalSearch',
);
const shortcutKeysGlobalLogout = defineModel<boolean>(
'shortcutKeysGlobalLogout',
);
const shortcutKeysGlobalPreferences = defineModel<boolean>(
'shortcutKeysGlobalPreferences',
);
const shortcutKeysGlobalLockScreen = defineModel<boolean>(
'shortcutKeysGlobalLockScreen',
);
const widgetGlobalSearch = defineModel<boolean>('widgetGlobalSearch');
const widgetFullscreen = defineModel<boolean>('widgetFullscreen');
const widgetLanguageToggle = defineModel<boolean>('widgetLanguageToggle');
const widgetNotification = defineModel<boolean>('widgetNotification');
const widgetThemeToggle = defineModel<boolean>('widgetThemeToggle');
const widgetAiAssistant = defineModel<boolean>('widgetAiAssistant');
const widgetSidebarToggle = defineModel<boolean>('widgetSidebarToggle');
const widgetLockScreen = defineModel<boolean>('widgetLockScreen');
const {
diffPreference,
isDark,
isFullContent,
isHeaderNav,
isMixedNav,
isSideMixedNav,
isSideMode,
isSideNav,
} = usePreferences();
const { copy } = useClipboard();
const activeTab = ref('appearance');
const tabs = computed((): SegmentedItem[] => {
return [
{
label: $t('preferences.appearance'),
value: 'appearance',
},
{
label: $t('preferences.layout'),
value: 'layout',
},
{
label: $t('preferences.shortcutKeys.title'),
value: 'shortcutKey',
},
{
label: $t('preferences.general'),
value: 'general',
},
];
});
const showBreadcrumbConfig = computed(() => {
return (
!isFullContent.value &&
!isMixedNav.value &&
!isHeaderNav.value &&
preferences.header.enable
);
});
const { openPreferences } = useOpenPreferences();
async function handleCopy() {
await copy(JSON.stringify(diffPreference.value, null, 2));
toast({
description: $t('preferences.copyPreferences'),
title: $t('preferences.copyPreferencesSuccess'),
});
}
async function handleClearCache() {
resetPreferences();
clearPreferencesCache();
emit('clearPreferencesAndLogout');
}
async function handleReset() {
if (!diffPreference.value) {
return;
}
resetPreferences();
await loadLocaleMessages(preferences.app.locale);
toast({
description: $t('preferences.resetTitle'),
title: $t('preferences.resetSuccess'),
});
}
</script>
<template>
<div class="z-100 fixed right-0 top-1/2">
<VbenSheet
v-model:open="openPreferences"
:description="$t('preferences.subtitle')"
:title="$t('preferences.title')"
>
<template #trigger>
<VbenButton
:title="$t('preferences.title')"
class="bg-primary flex-col-center h-12 w-12 cursor-pointer rounded-l-lg rounded-r-none border-none"
>
<IconSetting
class="duration-3000 fill-primary-foreground animate-spin text-2xl"
/>
</VbenButton>
</template>
<template #extra>
<div class="flex items-center">
<VbenIconButton
:disabled="!diffPreference"
:tooltip="$t('preferences.resetTip')"
class="relative"
>
<span
v-if="diffPreference"
class="bg-primary absolute right-0.5 top-0.5 h-2 w-2 rounded"
></span>
<IcRoundRestartAlt class="size-5" @click="handleReset" />
</VbenIconButton>
</div>
</template>
<div class="p-4 pt-4">
<VbenSegmented v-model="activeTab" :tabs="tabs">
<template #general>
<Block :title="$t('preferences.general')">
<General
v-model:app-dynamic-title="appDynamicTitle"
v-model:app-locale="appLocale"
/>
</Block>
<Block :title="$t('preferences.animation.title')">
<Animation
v-model:transition-enable="transitionEnable"
v-model:transition-loading="transitionLoading"
v-model:transition-name="transitionName"
v-model:transition-progress="transitionProgress"
/>
</Block>
</template>
<template #appearance>
<Block :title="$t('preferences.theme.title')">
<Theme
v-model="themeMode"
v-model:app-semi-dark-menu="appSemiDarkMenu"
/>
</Block>
<!-- <Block :title="$t('preferences.theme-color')">
<ThemeColor
v-model="themeColorPrimary"
:color-primary-presets="colorPrimaryPresets"
/>
</Block> -->
<Block :title="$t('preferences.theme.builtin.title')">
<BuiltinTheme
v-model="themeBuiltinType"
v-model:theme-color-primary="themeColorPrimary"
:is-dark="isDark"
/>
</Block>
<Block :title="$t('preferences.theme.radius')">
<Radius v-model="themeRadius" />
</Block>
<Block :title="$t('preferences.other')">
<ColorMode
v-model:app-color-gray-mode="appColorGrayMode"
v-model:app-color-weak-mode="appColorWeakMode"
/>
</Block>
</template>
<template #layout>
<Block :title="$t('preferences.layout')">
<Layout v-model="appLayout" />
</Block>
<Block :title="$t('preferences.content')">
<Content v-model="appContentCompact" />
</Block>
<Block :title="$t('preferences.sidebar.title')">
<Sidebar
v-model:sidebar-collapsed="sidebarCollapsed"
v-model:sidebar-collapsed-show-title="sidebarCollapsedShowTitle"
v-model:sidebar-enable="sidebarEnable"
v-model:sidebar-width="sidebarWidth"
:disabled="!isSideMode"
/>
</Block>
<Block :title="$t('preferences.header.title')">
<Header
v-model:headerEnable="headerEnable"
v-model:headerMode="headerMode"
:disabled="isFullContent"
/>
</Block>
<Block :title="$t('preferences.navigationMenu.title')">
<Navigation
v-model:navigation-accordion="navigationAccordion"
v-model:navigation-split="navigationSplit"
v-model:navigation-style-type="navigationStyleType"
:disabled="isFullContent"
:disabled-navigation-split="!isMixedNav"
/>
</Block>
<Block :title="$t('preferences.breadcrumb.title')">
<Breadcrumb
v-model:breadcrumb-enable="breadcrumbEnable"
v-model:breadcrumb-hide-only-one="breadcrumbHideOnlyOne"
v-model:breadcrumb-show-home="breadcrumbShowHome"
v-model:breadcrumb-show-icon="breadcrumbShowIcon"
v-model:breadcrumb-style-type="breadcrumbStyleType"
:disabled="
!showBreadcrumbConfig || !(isSideNav || isSideMixedNav)
"
/>
</Block>
<Block :title="$t('preferences.tabbar.title')">
<Tabbar
v-model:tabbar-enable="tabbarEnable"
2024-07-10 00:50:41 +08:00
v-model:tabbar-persist="tabbarPersist"
v-model:tabbar-show-icon="tabbarShowIcon"
/>
</Block>
<Block :title="$t('preferences.widget.title')">
<Widget
v-model:widget-ai-assistant="widgetAiAssistant"
v-model:widget-fullscreen="widgetFullscreen"
v-model:widget-global-search="widgetGlobalSearch"
v-model:widget-language-toggle="widgetLanguageToggle"
v-model:widget-lock-screen="widgetLockScreen"
v-model:widget-notification="widgetNotification"
v-model:widget-sidebar-toggle="widgetSidebarToggle"
v-model:widget-theme-toggle="widgetThemeToggle"
/>
</Block>
<Block :title="$t('preferences.footer.title')">
<Footer
v-model:footer-enable="footerEnable"
v-model:footer-fixed="footerFixed"
/>
</Block>
<Block :title="$t('preferences.copyright.title')">
<Copyright
v-model:copyright-company-name="copyrightCompanyName"
v-model:copyright-company-site-link="copyrightCompanySiteLink"
v-model:copyright-date="copyrightDate"
v-model:copyright-enable="copyrightEnable"
v-model:copyright-icp="copyrightIcp"
v-model:copyright-icp-link="copyrightIcpLink"
:disabled="!footerEnable"
/>
</Block>
</template>
<template #shortcutKey>
<Block :title="$t('preferences.shortcutKeys.global')">
<GlobalShortcutKeys
v-model:shortcut-keys-enable="shortcutKeysEnable"
v-model:shortcut-keys-global-search="shortcutKeysGlobalSearch"
v-model:shortcut-keys-lock-screen="shortcutKeysGlobalLockScreen"
v-model:shortcut-keys-logout="shortcutKeysGlobalLogout"
v-model:shortcut-keys-preferences="
shortcutKeysGlobalPreferences
"
/>
</Block>
</template>
</VbenSegmented>
</div>
<template #footer>
<VbenButton
:disabled="!diffPreference"
class="mx-4 w-full"
size="sm"
variant="default"
@click="handleCopy"
>
<IcRoundFolderCopy class="mr-2 size-3" />
{{ $t('preferences.copyPreferences') }}
</VbenButton>
<VbenButton
:disabled="!diffPreference"
class="mr-4 w-full"
size="sm"
variant="ghost"
@click="handleClearCache"
>
<IcRoundRestartAlt class="mr-2 size-4" />
{{ $t('preferences.clearAndLogout') }}
</VbenButton>
</template>
</VbenSheet>
</div>
</template>