Setting.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. <script setup lang="ts">
  2. import { ElDrawer, ElDivider, ElMessage } from 'element-plus'
  3. import { ref, unref, computed, watch } from 'vue'
  4. import { useCssVar, useClipboard } from '@vueuse/core'
  5. import { useI18n } from '@/hooks/web/useI18n'
  6. import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
  7. import { useDesign } from '@/hooks/web/useDesign'
  8. import { trim, setCssVar } from '@/utils'
  9. import { colorIsDark, lighten, hexToRGB } from '@/utils/color'
  10. import { useAppStore } from '@/store/modules/app'
  11. import { ThemeSwitch } from '@/components/ThemeSwitch'
  12. import ColorRadioPicker from './components/ColorRadioPicker.vue'
  13. import InterfaceDisplay from './components/InterfaceDisplay.vue'
  14. import LayoutRadioPicker from './components/LayoutRadioPicker.vue'
  15. const { t } = useI18n()
  16. const appStore = useAppStore()
  17. const { getPrefixCls } = useDesign()
  18. const prefixCls = getPrefixCls('setting')
  19. const layout = computed(() => appStore.getLayout)
  20. const drawer = ref(false)
  21. // 主题色相关
  22. const systemTheme = ref(appStore.getTheme.elColorPrimary)
  23. const setSystemTheme = (color: string) => {
  24. setCssVar('--el-color-primary', color)
  25. appStore.setTheme({ elColorPrimary: color })
  26. const leftMenuBgColor = useCssVar('--left-menu-bg-color', document.documentElement)
  27. setMenuTheme(trim(unref(leftMenuBgColor)))
  28. }
  29. // 头部主题相关
  30. const headerTheme = ref(appStore.getTheme.topHeaderBgColor || '')
  31. const setHeaderTheme = (color: string) => {
  32. const isDarkColor = colorIsDark(color)
  33. const textColor = isDarkColor ? '#fff' : 'inherit'
  34. const textHoverColor = isDarkColor ? lighten(color!, 6) : '#f6f6f6'
  35. const topToolBorderColor = isDarkColor ? color : '#eee'
  36. setCssVar('--top-header-bg-color', color)
  37. setCssVar('--top-header-text-color', textColor)
  38. setCssVar('--top-header-hover-color', textHoverColor)
  39. setCssVar('--top-tool-border-color', topToolBorderColor)
  40. appStore.setTheme({
  41. topHeaderBgColor: color,
  42. topHeaderTextColor: textColor,
  43. topHeaderHoverColor: textHoverColor,
  44. topToolBorderColor
  45. })
  46. if (unref(layout) === 'top') {
  47. setMenuTheme(color)
  48. }
  49. }
  50. // 菜单主题相关
  51. const menuTheme = ref(appStore.getTheme.leftMenuBgColor || '')
  52. const setMenuTheme = (color: string) => {
  53. const primaryColor = useCssVar('--el-color-primary', document.documentElement)
  54. const isDarkColor = colorIsDark(color)
  55. const theme: Recordable = {
  56. // 左侧菜单边框颜色
  57. leftMenuBorderColor: isDarkColor ? 'inherit' : '#eee',
  58. // 左侧菜单背景颜色
  59. leftMenuBgColor: color,
  60. // 左侧菜单浅色背景颜色
  61. leftMenuBgLightColor: isDarkColor ? lighten(color!, 6) : color,
  62. // 左侧菜单选中背景颜色
  63. leftMenuBgActiveColor: isDarkColor
  64. ? 'var(--el-color-primary)'
  65. : hexToRGB(unref(primaryColor), 0.1),
  66. // 左侧菜单收起选中背景颜色
  67. leftMenuCollapseBgActiveColor: isDarkColor
  68. ? 'var(--el-color-primary)'
  69. : hexToRGB(unref(primaryColor), 0.1),
  70. // 左侧菜单字体颜色
  71. leftMenuTextColor: isDarkColor ? '#bfcbd9' : '#333',
  72. // 左侧菜单选中字体颜色
  73. leftMenuTextActiveColor: isDarkColor ? '#fff' : 'var(--el-color-primary)',
  74. // logo字体颜色
  75. logoTitleTextColor: isDarkColor ? '#fff' : 'inherit',
  76. // logo边框颜色
  77. logoBorderColor: isDarkColor ? color : '#eee'
  78. }
  79. appStore.setTheme(theme)
  80. appStore.setCssVarTheme()
  81. }
  82. if (layout.value === 'top' && !appStore.getIsDark) {
  83. headerTheme.value = '#fff'
  84. setHeaderTheme('#fff')
  85. }
  86. // 监听layout变化,重置一些主题色
  87. watch(
  88. () => layout.value,
  89. (n) => {
  90. if (n === 'top' && !appStore.getIsDark) {
  91. headerTheme.value = '#fff'
  92. setHeaderTheme('#fff')
  93. } else {
  94. setMenuTheme(unref(menuTheme))
  95. }
  96. }
  97. )
  98. // 拷贝
  99. const copyConfig = async () => {
  100. const { copy, copied, isSupported } = useClipboard({
  101. source: `
  102. // 面包屑
  103. breadcrumb: ${appStore.getBreadcrumb},
  104. // 面包屑图标
  105. breadcrumbIcon: ${appStore.getBreadcrumbIcon},
  106. // 折叠图标
  107. hamburger: ${appStore.getHamburger},
  108. // 全屏图标
  109. screenfull: ${appStore.getScreenfull},
  110. // 尺寸图标
  111. size: ${appStore.getSize},
  112. // 多语言图标
  113. locale: ${appStore.getLocale},
  114. // 标签页
  115. tagsView: ${appStore.getTagsView},
  116. // 标签页图标
  117. getTagsViewIcon: ${appStore.getTagsViewIcon},
  118. // logo
  119. logo: ${appStore.getLogo},
  120. // 菜单手风琴
  121. uniqueOpened: ${appStore.getUniqueOpened},
  122. // 固定header
  123. fixedHeader: ${appStore.getFixedHeader},
  124. // 页脚
  125. footer: ${appStore.getFooter},
  126. // 灰色模式
  127. greyMode: ${appStore.getGreyMode},
  128. // layout布局
  129. layout: '${appStore.getLayout}',
  130. // 暗黑模式
  131. isDark: ${appStore.getIsDark},
  132. // 组件尺寸
  133. currentSize: '${appStore.getCurrentSize}',
  134. // 主题相关
  135. theme: {
  136. // 主题色
  137. elColorPrimary: '${appStore.getTheme.elColorPrimary}',
  138. // 左侧菜单边框颜色
  139. leftMenuBorderColor: '${appStore.getTheme.leftMenuBorderColor}',
  140. // 左侧菜单背景颜色
  141. leftMenuBgColor: '${appStore.getTheme.leftMenuBgColor}',
  142. // 左侧菜单浅色背景颜色
  143. leftMenuBgLightColor: '${appStore.getTheme.leftMenuBgLightColor}',
  144. // 左侧菜单选中背景颜色
  145. leftMenuBgActiveColor: '${appStore.getTheme.leftMenuBgActiveColor}',
  146. // 左侧菜单收起选中背景颜色
  147. leftMenuCollapseBgActiveColor: '${appStore.getTheme.leftMenuCollapseBgActiveColor}',
  148. // 左侧菜单字体颜色
  149. leftMenuTextColor: '${appStore.getTheme.leftMenuTextColor}',
  150. // 左侧菜单选中字体颜色
  151. leftMenuTextActiveColor: '${appStore.getTheme.leftMenuTextActiveColor}',
  152. // logo字体颜色
  153. logoTitleTextColor: '${appStore.getTheme.logoTitleTextColor}',
  154. // logo边框颜色
  155. logoBorderColor: '${appStore.getTheme.logoBorderColor}',
  156. // 头部背景颜色
  157. topHeaderBgColor: '${appStore.getTheme.topHeaderBgColor}',
  158. // 头部字体颜色
  159. topHeaderTextColor: '${appStore.getTheme.topHeaderTextColor}',
  160. // 头部悬停颜色
  161. topHeaderHoverColor: '${appStore.getTheme.topHeaderHoverColor}',
  162. // 头部边框颜色
  163. topToolBorderColor: '${appStore.getTheme.topToolBorderColor}'
  164. }
  165. `
  166. })
  167. if (!isSupported) {
  168. ElMessage.error(t('setting.copyFailed'))
  169. } else {
  170. await copy()
  171. if (unref(copied)) {
  172. ElMessage.success(t('setting.copySuccess'))
  173. }
  174. }
  175. }
  176. // 清空缓存
  177. const clear = () => {
  178. const { wsCache } = useCache()
  179. wsCache.delete(CACHE_KEY.LAYOUT)
  180. wsCache.delete(CACHE_KEY.THEME)
  181. wsCache.delete(CACHE_KEY.IS_DARK)
  182. window.location.reload()
  183. }
  184. </script>
  185. <template>
  186. <div
  187. :class="prefixCls"
  188. class="fixed top-[45%] right-0 w-40px h-40px text-center leading-40px bg-[var(--el-color-primary)] cursor-pointer"
  189. @click="drawer = true"
  190. >
  191. <Icon icon="ep:setting" color="#fff" />
  192. </div>
  193. <ElDrawer v-model="drawer" direction="rtl" size="350px" :z-index="4000">
  194. <template #header>
  195. <span class="text-16px font-700">{{ t('setting.projectSetting') }}</span>
  196. </template>
  197. <div class="text-center">
  198. <!-- 主题 -->
  199. <ElDivider>{{ t('setting.theme') }}</ElDivider>
  200. <ThemeSwitch />
  201. <!-- 布局 -->
  202. <ElDivider>{{ t('setting.layout') }}</ElDivider>
  203. <LayoutRadioPicker />
  204. <!-- 系统主题 -->
  205. <ElDivider>{{ t('setting.systemTheme') }}</ElDivider>
  206. <ColorRadioPicker
  207. v-model="systemTheme"
  208. :schema="[
  209. '#409eff',
  210. '#009688',
  211. '#536dfe',
  212. '#ff5c93',
  213. '#ee4f12',
  214. '#0096c7',
  215. '#9c27b0',
  216. '#ff9800'
  217. ]"
  218. @change="setSystemTheme"
  219. />
  220. <!-- 头部主题 -->
  221. <ElDivider>{{ t('setting.headerTheme') }}</ElDivider>
  222. <ColorRadioPicker
  223. v-model="headerTheme"
  224. :schema="[
  225. '#fff',
  226. '#151515',
  227. '#5172dc',
  228. '#e74c3c',
  229. '#24292e',
  230. '#394664',
  231. '#009688',
  232. '#383f45'
  233. ]"
  234. @change="setHeaderTheme"
  235. />
  236. <!-- 菜单主题 -->
  237. <template v-if="layout !== 'top'">
  238. <ElDivider>{{ t('setting.menuTheme') }}</ElDivider>
  239. <ColorRadioPicker
  240. v-model="menuTheme"
  241. :schema="[
  242. '#fff',
  243. '#001529',
  244. '#212121',
  245. '#273352',
  246. '#191b24',
  247. '#383f45',
  248. '#001628',
  249. '#344058'
  250. ]"
  251. @change="setMenuTheme"
  252. />
  253. </template>
  254. </div>
  255. <!-- 界面显示 -->
  256. <ElDivider>{{ t('setting.interfaceDisplay') }}</ElDivider>
  257. <InterfaceDisplay />
  258. <ElDivider />
  259. <div>
  260. <ElButton type="primary" class="w-full" @click="copyConfig">{{ t('setting.copy') }}</ElButton>
  261. </div>
  262. <div class="mt-5px">
  263. <ElButton type="danger" class="w-full" @click="clear">
  264. {{ t('setting.clearAndReset') }}
  265. </ElButton>
  266. </div>
  267. </ElDrawer>
  268. </template>
  269. <style lang="scss" scoped>
  270. $prefix-cls: #{$namespace}-setting;
  271. .#{$prefix-cls} {
  272. border-radius: 6px 0 0 6px;
  273. }
  274. </style>