preferences.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <script setup lang="ts">
  2. import type {
  3. ContentCompactType,
  4. LayoutHeaderModeType,
  5. LayoutType,
  6. SupportedLanguagesType,
  7. ThemeModeType,
  8. } from '@vben/types';
  9. import type {
  10. BreadcrumbStyleType,
  11. NavigationStyleType,
  12. } from '@vben-core/preferences';
  13. import type { SegmentedItem } from '@vben-core/shadcn-ui';
  14. import { computed } from 'vue';
  15. import { $t } from '@vben/locales';
  16. import { IcRoundFolderCopy, IcRoundRestartAlt } from '@vben-core/iconify';
  17. import {
  18. preferences,
  19. resetPreferences,
  20. usePreferences,
  21. } from '@vben-core/preferences';
  22. import {
  23. VbenButton,
  24. VbenIconButton,
  25. VbenSegmented,
  26. VbenSheet,
  27. toast,
  28. } from '@vben-core/shadcn-ui';
  29. import { useClipboard } from '@vueuse/core';
  30. import {
  31. Animation,
  32. Block,
  33. Breadcrumb,
  34. ColorMode,
  35. Content,
  36. Footer,
  37. General,
  38. GlobalShortcutKeys,
  39. Header,
  40. Layout,
  41. Navigation,
  42. Sidebar,
  43. Tabbar,
  44. Theme,
  45. ThemeColor,
  46. } from './blocks';
  47. import Trigger from './trigger.vue';
  48. import { useOpenPreferences } from './use-open-preferences';
  49. withDefaults(defineProps<{ colorPrimaryPresets: string[] }>(), {
  50. colorPrimaryPresets: () => [],
  51. });
  52. const appThemeMode = defineModel<ThemeModeType>('appThemeMode');
  53. const appLocale = defineModel<SupportedLanguagesType>('appLocale');
  54. const appDynamicTitle = defineModel<boolean>('appDynamicTitle');
  55. const appAiAssistant = defineModel<boolean>('appAiAssistant');
  56. const appLayout = defineModel<LayoutType>('appLayout');
  57. const appColorGrayMode = defineModel<boolean>('appColorGrayMode');
  58. const appColorWeakMode = defineModel<boolean>('appColorWeakMode');
  59. const appSemiDarkMenu = defineModel<boolean>('appSemiDarkMenu');
  60. const appContentCompact = defineModel<ContentCompactType>('appContentCompact');
  61. const transitionProgress = defineModel<boolean>('transitionProgress');
  62. const transitionName = defineModel<string>('transitionName');
  63. const transitionEnable = defineModel<boolean>('transitionEnable');
  64. const themeColorPrimary = defineModel<string>('themeColorPrimary');
  65. const sidebarEnable = defineModel<boolean>('sidebarEnable');
  66. const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
  67. const sidebarCollapsedShowTitle = defineModel<boolean>(
  68. 'sidebarCollapsedShowTitle',
  69. );
  70. const headerEnable = defineModel<boolean>('headerEnable');
  71. const headerMode = defineModel<LayoutHeaderModeType>('headerMode');
  72. const breadcrumbEnable = defineModel<boolean>('breadcrumbEnable');
  73. const breadcrumbShowIcon = defineModel<boolean>('breadcrumbShowIcon');
  74. const breadcrumbShowHome = defineModel<boolean>('breadcrumbShowHome');
  75. const breadcrumbStyleType = defineModel<BreadcrumbStyleType>(
  76. 'breadcrumbStyleType',
  77. );
  78. const breadcrumbHideOnlyOne = defineModel<boolean>('breadcrumbHideOnlyOne');
  79. const tabbarEnable = defineModel<boolean>('tabbarEnable');
  80. const tabbarShowIcon = defineModel<boolean>('tabbarShowIcon');
  81. const navigationStyleType = defineModel<NavigationStyleType>(
  82. 'navigationStyleType',
  83. );
  84. const navigationSplit = defineModel<boolean>('navigationSplit');
  85. const navigationAccordion = defineModel<boolean>('navigationAccordion');
  86. // const logoVisible = defineModel<boolean>('logoVisible');
  87. const footerEnable = defineModel<boolean>('footerEnable');
  88. const footerFixed = defineModel<boolean>('footerFixed');
  89. const shortcutKeysEnable = defineModel<boolean>('shortcutKeysEnable');
  90. const shortcutKeysGlobalSearch = defineModel<boolean>(
  91. 'shortcutKeysGlobalSearch',
  92. );
  93. const shortcutKeysGlobalLogout = defineModel<boolean>(
  94. 'shortcutKeysGlobalLogout',
  95. );
  96. const shortcutKeysGlobalPreferences = defineModel<boolean>(
  97. 'shortcutKeysGlobalPreferences',
  98. );
  99. const {
  100. diffPreference,
  101. isFullContent,
  102. isHeaderNav,
  103. isMixedNav,
  104. isSideMixedNav,
  105. isSideMode,
  106. isSideNav,
  107. } = usePreferences();
  108. const { copy } = useClipboard();
  109. const tabs = computed((): SegmentedItem[] => {
  110. return [
  111. {
  112. label: $t('preferences.general'),
  113. value: 'general',
  114. },
  115. {
  116. label: $t('preferences.appearance'),
  117. value: 'appearance',
  118. },
  119. {
  120. label: $t('preferences.layout'),
  121. value: 'layout',
  122. },
  123. {
  124. label: $t('preferences.shortcut-keys.title'),
  125. value: 'shortcutKey',
  126. },
  127. ];
  128. });
  129. const showBreadcrumbConfig = computed(() => {
  130. return (
  131. !isFullContent.value &&
  132. !isMixedNav.value &&
  133. !isHeaderNav.value &&
  134. preferences.header.enable
  135. );
  136. });
  137. const { openPreferences } = useOpenPreferences();
  138. async function handleCopy() {
  139. await copy(JSON.stringify(diffPreference.value, null, 2));
  140. toast($t('preferences.copy-success'));
  141. }
  142. function handleReset() {
  143. if (!diffPreference.value) {
  144. return;
  145. }
  146. resetPreferences();
  147. toast($t('preferences.reset-success'));
  148. }
  149. </script>
  150. <template>
  151. <div class="z-100 fixed right-0 top-1/2">
  152. <VbenSheet
  153. v-model:open="openPreferences"
  154. :description="$t('preferences.preferences-subtitle')"
  155. :title="$t('preferences.preferences')"
  156. >
  157. <template #trigger>
  158. <Trigger />
  159. </template>
  160. <template #extra>
  161. <VbenIconButton
  162. :disabled="!diffPreference"
  163. :tooltip="$t('preferences.reset-tip')"
  164. class="relative"
  165. >
  166. <span
  167. v-if="diffPreference"
  168. class="bg-primary absolute right-0.5 top-0.5 h-2 w-2 rounded"
  169. ></span>
  170. <IcRoundRestartAlt class="size-5" @click="handleReset" />
  171. </VbenIconButton>
  172. </template>
  173. <div class="p-4 pt-4">
  174. <VbenSegmented :tabs="tabs" default-value="general">
  175. <template #appearance>
  176. <Block :title="$t('preferences.theme')">
  177. <Theme
  178. v-model="appThemeMode"
  179. v-model:app-semi-dark-menu="appSemiDarkMenu"
  180. />
  181. </Block>
  182. <Block :title="$t('preferences.theme-color')">
  183. <ThemeColor
  184. v-model="themeColorPrimary"
  185. :color-primary-presets="colorPrimaryPresets"
  186. />
  187. </Block>
  188. <Block :title="$t('preferences.other')">
  189. <ColorMode
  190. v-model:app-color-gray-mode="appColorGrayMode"
  191. v-model:app-color-weak-mode="appColorWeakMode"
  192. />
  193. </Block>
  194. </template>
  195. <template #layout>
  196. <Block :title="$t('preferences.layout')">
  197. <Layout v-model="appLayout" />
  198. </Block>
  199. <Block :title="$t('preferences.content')">
  200. <Content v-model="appContentCompact" />
  201. </Block>
  202. <Block :title="$t('preferences.sidebar')">
  203. <Sidebar
  204. v-model:sidebar-collapsed="sidebarCollapsed"
  205. v-model:sidebar-collapsed-show-title="sidebarCollapsedShowTitle"
  206. v-model:sidebar-enable="sidebarEnable"
  207. :disabled="!isSideMode"
  208. />
  209. </Block>
  210. <Block :title="$t('preferences.header')">
  211. <Header
  212. v-model:headerEnable="headerEnable"
  213. v-model:headerMode="headerMode"
  214. :disabled="isFullContent"
  215. />
  216. </Block>
  217. <Block :title="$t('preferences.navigation-menu')">
  218. <Navigation
  219. v-model:navigation-accordion="navigationAccordion"
  220. v-model:navigation-split="navigationSplit"
  221. v-model:navigation-style-type="navigationStyleType"
  222. :disabled="isFullContent"
  223. :disabled-navigation-split="!isMixedNav"
  224. />
  225. </Block>
  226. <Block :title="$t('preferences.breadcrumb')">
  227. <Breadcrumb
  228. v-model:breadcrumb-enable="breadcrumbEnable"
  229. v-model:breadcrumb-hide-only-one="breadcrumbHideOnlyOne"
  230. v-model:breadcrumb-show-home="breadcrumbShowHome"
  231. v-model:breadcrumb-show-icon="breadcrumbShowIcon"
  232. v-model:breadcrumb-style-type="breadcrumbStyleType"
  233. :disabled="
  234. !showBreadcrumbConfig || !(isSideNav || isSideMixedNav)
  235. "
  236. />
  237. </Block>
  238. <Block :title="$t('preferences.tabs')">
  239. <Tabbar
  240. v-model:tabbar-enable="tabbarEnable"
  241. v-model:tabbar-show-icon="tabbarShowIcon"
  242. />
  243. </Block>
  244. <Block :title="$t('preferences.footer')">
  245. <Footer
  246. v-model:footer-enable="footerEnable"
  247. v-model:footer-fixed="footerFixed"
  248. />
  249. </Block>
  250. </template>
  251. <template #general>
  252. <Block :title="$t('preferences.general')">
  253. <General
  254. v-model:app-ai-assistant="appAiAssistant"
  255. v-model:app-dynamic-title="appDynamicTitle"
  256. v-model:app-locale="appLocale"
  257. />
  258. </Block>
  259. <Block :title="$t('preferences.animation')">
  260. <Animation
  261. v-model:transition-enable="transitionEnable"
  262. v-model:transition-name="transitionName"
  263. v-model:transition-progress="transitionProgress"
  264. />
  265. </Block>
  266. </template>
  267. <template #shortcutKey>
  268. <Block :title="$t('preferences.shortcut-keys.global')">
  269. <GlobalShortcutKeys
  270. v-model:shortcut-keys-enable="shortcutKeysEnable"
  271. v-model:shortcut-keys-global-search="shortcutKeysGlobalSearch"
  272. v-model:shortcut-keys-logout="shortcutKeysGlobalLogout"
  273. v-model:shortcut-keys-preferences="
  274. shortcutKeysGlobalPreferences
  275. "
  276. />
  277. </Block>
  278. </template>
  279. </VbenSegmented>
  280. </div>
  281. <template #footer>
  282. <VbenButton
  283. :disabled="!diffPreference"
  284. class="mx-6 w-full"
  285. size="sm"
  286. variant="default"
  287. @click="handleCopy"
  288. >
  289. <IcRoundFolderCopy class="mr-2 size-3" />
  290. {{ $t('preferences.copy') }}
  291. </VbenButton>
  292. </template>
  293. </VbenSheet>
  294. </div>
  295. </template>