preference.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. <script setup lang="ts">
  2. import type { LayoutHeaderMode, LayoutType } from '@vben/types';
  3. import type { SegmentedItem } from '@vben-core/shadcn-ui';
  4. import { IcRoundFolderCopy, IcRoundRestartAlt } from '@vben-core/iconify';
  5. import {
  6. VbenButton,
  7. VbenIconButton,
  8. VbenSegmented,
  9. VbenSheet,
  10. toast,
  11. } from '@vben-core/shadcn-ui';
  12. import { $t } from '@vben/locales';
  13. import { preference, resetPreference, usePreference } from '@vben/preference';
  14. import { useClipboard } from '@vueuse/core';
  15. import { computed } from 'vue';
  16. import {
  17. Animation,
  18. Block,
  19. Breadcrumb,
  20. ColorMode,
  21. Content,
  22. Footer,
  23. General,
  24. Header,
  25. Layout,
  26. Navigation,
  27. Sidebar,
  28. Tabs,
  29. Theme,
  30. ThemeColor,
  31. } from './blocks';
  32. import Trigger from './trigger.vue';
  33. import { useOpenPreference } from './use-open-preference';
  34. withDefaults(defineProps<{ colorPrimaryPresets: string[] }>(), {
  35. colorPrimaryPresets: () => [],
  36. });
  37. const theme = defineModel<string>('theme');
  38. const locale = defineModel<string>('locale');
  39. const dynamicTitle = defineModel<boolean>('dynamicTitle');
  40. const semiDarkMenu = defineModel<boolean>('semiDarkMenu');
  41. const breadcrumbVisible = defineModel<boolean>('breadcrumbVisible');
  42. const breadcrumbIcon = defineModel<boolean>('breadcrumbIcon');
  43. const breadcrumbHome = defineModel<boolean>('breadcrumbHome');
  44. const breadcrumbStyle = defineModel<string>('breadcrumbStyle');
  45. const breadcrumbHideOnlyOne = defineModel<boolean>('breadcrumbHideOnlyOne');
  46. const sideCollapseShowTitle = defineModel<boolean>('sideCollapseShowTitle');
  47. const sideCollapse = defineModel<boolean>('sideCollapse');
  48. const colorWeakMode = defineModel<boolean>('colorWeakMode');
  49. const colorGrayMode = defineModel<boolean>('colorGrayMode');
  50. const colorPrimary = defineModel<string>('colorPrimary');
  51. const navigationStyle = defineModel<string>('navigationStyle');
  52. const navigationSplit = defineModel<boolean>('navigationSplit');
  53. const navigationAccordion = defineModel<boolean>('navigationAccordion');
  54. const pageProgress = defineModel<boolean>('pageProgress');
  55. const pageTransition = defineModel<string>('pageTransition');
  56. const pageTransitionEnable = defineModel<boolean>('pageTransitionEnable');
  57. const layout = defineModel<LayoutType>('layout');
  58. const contentCompact = defineModel<string>('contentCompact');
  59. const sideVisible = defineModel<boolean>('sideVisible');
  60. const shortcutKeys = defineModel<boolean>('shortcutKeys');
  61. const tabsVisible = defineModel<boolean>('tabsVisible');
  62. const tabsIcon = defineModel<boolean>('tabsIcon');
  63. // const logoVisible = defineModel<boolean>('logoVisible');
  64. const headerVisible = defineModel<boolean>('headerVisible');
  65. const headerMode = defineModel<LayoutHeaderMode>('headerMode');
  66. const footerVisible = defineModel<boolean>('footerVisible');
  67. const footerFixed = defineModel<boolean>('footerFixed');
  68. const {
  69. diffPreference,
  70. isFullContent,
  71. isHeaderNav,
  72. isMixedNav,
  73. isSideMixedNav,
  74. isSideMode,
  75. isSideNav,
  76. } = usePreference();
  77. const { copy } = useClipboard();
  78. const tabs = computed((): SegmentedItem[] => {
  79. return [
  80. {
  81. label: $t('preference.appearance'),
  82. value: 'appearance',
  83. },
  84. {
  85. label: $t('preference.layout'),
  86. value: 'layout',
  87. },
  88. {
  89. label: $t('preference.general'),
  90. value: 'general',
  91. },
  92. // {
  93. // label: $t('preference.shortcut-key'),
  94. // value: 'shortcutKey',
  95. // },
  96. ];
  97. });
  98. const showBreadcrumbConfig = computed(() => {
  99. return (
  100. !isFullContent.value &&
  101. !isMixedNav.value &&
  102. !isHeaderNav.value &&
  103. preference.headerVisible
  104. );
  105. });
  106. const { openPreference } = useOpenPreference();
  107. async function handleCopy() {
  108. await copy(JSON.stringify(diffPreference.value, null, 2));
  109. toast($t('preference.copy-success'));
  110. }
  111. function handleReset() {
  112. if (!diffPreference.value) {
  113. return;
  114. }
  115. resetPreference();
  116. toast($t('preference.reset-success'));
  117. }
  118. </script>
  119. <template>
  120. <div class="z-100 fixed right-0 top-1/3">
  121. <VbenSheet
  122. v-model:open="openPreference"
  123. :description="$t('preference.preferences-subtitle')"
  124. :title="$t('preference.preferences')"
  125. >
  126. <template #trigger>
  127. <Trigger />
  128. </template>
  129. <template #extra>
  130. <VbenIconButton
  131. class="relative"
  132. :disabled="!diffPreference"
  133. :tooltip="$t('preference.reset-tip')"
  134. >
  135. <span
  136. v-if="diffPreference"
  137. class="bg-primary absolute right-0.5 top-0.5 h-2 w-2 rounded"
  138. ></span>
  139. <IcRoundRestartAlt class="size-5" @click="handleReset" />
  140. </VbenIconButton>
  141. </template>
  142. <div class="p-5 pt-4">
  143. <VbenSegmented :tabs="tabs" default-value="appearance">
  144. <template #appearance>
  145. <Block :title="$t('preference.theme')">
  146. <Theme v-model="theme" v-model:semi-dark-menu="semiDarkMenu" />
  147. </Block>
  148. <Block :title="$t('preference.theme-color')">
  149. <ThemeColor
  150. v-model="colorPrimary"
  151. :color-primary-presets="colorPrimaryPresets"
  152. />
  153. </Block>
  154. <Block :title="$t('preference.other')">
  155. <ColorMode
  156. v-model:color-gray-mode="colorGrayMode"
  157. v-model:color-weak-mode="colorWeakMode"
  158. />
  159. </Block>
  160. </template>
  161. <template #layout>
  162. <Block :title="$t('preference.layout')">
  163. <Layout v-model="layout" />
  164. </Block>
  165. <Block :title="$t('preference.content')">
  166. <Content v-model="contentCompact" />
  167. </Block>
  168. <Block :title="$t('preference.sidebar')">
  169. <Sidebar
  170. v-model:side-visible="sideVisible"
  171. v-model:side-collapse="sideCollapse"
  172. v-model:side-collapse-show-title="sideCollapseShowTitle"
  173. :disabled="!isSideMode"
  174. />
  175. </Block>
  176. <Block :title="$t('preference.header')">
  177. <Header
  178. v-model:header-visible="headerVisible"
  179. v-model:headerMode="headerMode"
  180. :disabled="isFullContent"
  181. />
  182. </Block>
  183. <Block :title="$t('preference.navigation-menu')">
  184. <Navigation
  185. v-model:navigation-style="navigationStyle"
  186. v-model:navigation-split="navigationSplit"
  187. v-model:navigation-accordion="navigationAccordion"
  188. :disabled="isFullContent"
  189. :disabled-navigation-split="!isMixedNav"
  190. />
  191. </Block>
  192. <Block :title="$t('preference.breadcrumb')">
  193. <Breadcrumb
  194. v-model:breadcrumb-visible="breadcrumbVisible"
  195. v-model:breadcrumb-icon="breadcrumbIcon"
  196. v-model:breadcrumb-style="breadcrumbStyle"
  197. v-model:breadcrumb-home="breadcrumbHome"
  198. v-model:breadcrumb-hide-only-one="breadcrumbHideOnlyOne"
  199. :disabled="
  200. !showBreadcrumbConfig || !(isSideNav || isSideMixedNav)
  201. "
  202. />
  203. </Block>
  204. <Block :title="$t('preference.tabs')">
  205. <Tabs
  206. v-model:tabs-visible="tabsVisible"
  207. v-model:tabs-icon="tabsIcon"
  208. />
  209. </Block>
  210. <Block :title="$t('preference.footer')">
  211. <Footer
  212. v-model:footer-visible="footerVisible"
  213. v-model:footer-fixed="footerFixed"
  214. />
  215. </Block>
  216. </template>
  217. <template #general>
  218. <Block :title="$t('preference.general')">
  219. <General
  220. v-model:locale="locale"
  221. v-model:dynamic-title="dynamicTitle"
  222. v-model:shortcut-keys="shortcutKeys"
  223. />
  224. </Block>
  225. <Block :title="$t('preference.animation')">
  226. <Animation
  227. v-model:page-progress="pageProgress"
  228. v-model:page-transition="pageTransition"
  229. v-model:page-transition-enable="pageTransitionEnable"
  230. />
  231. </Block>
  232. </template>
  233. <template #shortcutKey>
  234. <Block :title="$t('preference.general')">
  235. <General
  236. v-model:locale="locale"
  237. v-model:dynamic-title="dynamicTitle"
  238. v-model:shortcut-keys="shortcutKeys"
  239. />
  240. </Block>
  241. <Block :title="$t('preference.animation')">
  242. <Animation
  243. v-model:page-progress="pageProgress"
  244. v-model:page-transition="pageTransition"
  245. v-model:page-transition-enable="pageTransitionEnable"
  246. />
  247. </Block>
  248. </template>
  249. </VbenSegmented>
  250. </div>
  251. <template #footer>
  252. <VbenButton
  253. class="mx-6 w-full"
  254. variant="default"
  255. size="sm"
  256. :disabled="!diffPreference"
  257. @click="handleCopy"
  258. >
  259. <IcRoundFolderCopy class="mr-2 size-3" />
  260. {{ $t('preference.copy') }}
  261. </VbenButton>
  262. </template>
  263. </VbenSheet>
  264. </div>
  265. </template>