Setting.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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 '@/layout/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. message: ${appStore.getMessage},
  116. // 标签页
  117. tagsView: ${appStore.getTagsView},
  118. // 标签页图标
  119. getTagsViewIcon: ${appStore.getTagsViewIcon},
  120. // logo
  121. logo: ${appStore.getLogo},
  122. // 菜单手风琴
  123. uniqueOpened: ${appStore.getUniqueOpened},
  124. // 固定header
  125. fixedHeader: ${appStore.getFixedHeader},
  126. // 页脚
  127. footer: ${appStore.getFooter},
  128. // 灰色模式
  129. greyMode: ${appStore.getGreyMode},
  130. // layout布局
  131. layout: '${appStore.getLayout}',
  132. // 暗黑模式
  133. isDark: ${appStore.getIsDark},
  134. // 组件尺寸
  135. currentSize: '${appStore.getCurrentSize}',
  136. // 主题相关
  137. theme: {
  138. // 主题色
  139. elColorPrimary: '${appStore.getTheme.elColorPrimary}',
  140. // 左侧菜单边框颜色
  141. leftMenuBorderColor: '${appStore.getTheme.leftMenuBorderColor}',
  142. // 左侧菜单背景颜色
  143. leftMenuBgColor: '${appStore.getTheme.leftMenuBgColor}',
  144. // 左侧菜单浅色背景颜色
  145. leftMenuBgLightColor: '${appStore.getTheme.leftMenuBgLightColor}',
  146. // 左侧菜单选中背景颜色
  147. leftMenuBgActiveColor: '${appStore.getTheme.leftMenuBgActiveColor}',
  148. // 左侧菜单收起选中背景颜色
  149. leftMenuCollapseBgActiveColor: '${appStore.getTheme.leftMenuCollapseBgActiveColor}',
  150. // 左侧菜单字体颜色
  151. leftMenuTextColor: '${appStore.getTheme.leftMenuTextColor}',
  152. // 左侧菜单选中字体颜色
  153. leftMenuTextActiveColor: '${appStore.getTheme.leftMenuTextActiveColor}',
  154. // logo字体颜色
  155. logoTitleTextColor: '${appStore.getTheme.logoTitleTextColor}',
  156. // logo边框颜色
  157. logoBorderColor: '${appStore.getTheme.logoBorderColor}',
  158. // 头部背景颜色
  159. topHeaderBgColor: '${appStore.getTheme.topHeaderBgColor}',
  160. // 头部字体颜色
  161. topHeaderTextColor: '${appStore.getTheme.topHeaderTextColor}',
  162. // 头部悬停颜色
  163. topHeaderHoverColor: '${appStore.getTheme.topHeaderHoverColor}',
  164. // 头部边框颜色
  165. topToolBorderColor: '${appStore.getTheme.topToolBorderColor}'
  166. }
  167. `
  168. })
  169. if (!isSupported) {
  170. ElMessage.error(t('setting.copyFailed'))
  171. } else {
  172. await copy()
  173. if (unref(copied)) {
  174. ElMessage.success(t('setting.copySuccess'))
  175. }
  176. }
  177. }
  178. // 清空缓存
  179. const clear = () => {
  180. const { wsCache } = useCache()
  181. wsCache.delete(CACHE_KEY.LAYOUT)
  182. wsCache.delete(CACHE_KEY.THEME)
  183. wsCache.delete(CACHE_KEY.IS_DARK)
  184. window.location.reload()
  185. }
  186. </script>
  187. <template>
  188. <div
  189. :class="prefixCls"
  190. class="fixed top-[45%] right-0 w-40px h-40px text-center leading-40px bg-[var(--el-color-primary)] cursor-pointer"
  191. @click="drawer = true"
  192. >
  193. <Icon icon="ep:setting" color="#fff" />
  194. </div>
  195. <ElDrawer v-model="drawer" direction="rtl" size="350px" :z-index="4000">
  196. <template #header>
  197. <span class="text-16px font-700">{{ t('setting.projectSetting') }}</span>
  198. </template>
  199. <div class="text-center">
  200. <!-- 主题 -->
  201. <ElDivider>{{ t('setting.theme') }}</ElDivider>
  202. <ThemeSwitch />
  203. <!-- 布局 -->
  204. <ElDivider>{{ t('setting.layout') }}</ElDivider>
  205. <LayoutRadioPicker />
  206. <!-- 系统主题 -->
  207. <ElDivider>{{ t('setting.systemTheme') }}</ElDivider>
  208. <ColorRadioPicker
  209. v-model="systemTheme"
  210. :schema="[
  211. '#409eff',
  212. '#009688',
  213. '#536dfe',
  214. '#ff5c93',
  215. '#ee4f12',
  216. '#0096c7',
  217. '#9c27b0',
  218. '#ff9800'
  219. ]"
  220. @change="setSystemTheme"
  221. />
  222. <!-- 头部主题 -->
  223. <ElDivider>{{ t('setting.headerTheme') }}</ElDivider>
  224. <ColorRadioPicker
  225. v-model="headerTheme"
  226. :schema="[
  227. '#fff',
  228. '#151515',
  229. '#5172dc',
  230. '#e74c3c',
  231. '#24292e',
  232. '#394664',
  233. '#009688',
  234. '#383f45'
  235. ]"
  236. @change="setHeaderTheme"
  237. />
  238. <!-- 菜单主题 -->
  239. <template v-if="layout !== 'top'">
  240. <ElDivider>{{ t('setting.menuTheme') }}</ElDivider>
  241. <ColorRadioPicker
  242. v-model="menuTheme"
  243. :schema="[
  244. '#fff',
  245. '#001529',
  246. '#212121',
  247. '#273352',
  248. '#191b24',
  249. '#383f45',
  250. '#001628',
  251. '#344058'
  252. ]"
  253. @change="setMenuTheme"
  254. />
  255. </template>
  256. </div>
  257. <!-- 界面显示 -->
  258. <ElDivider>{{ t('setting.interfaceDisplay') }}</ElDivider>
  259. <InterfaceDisplay />
  260. <ElDivider />
  261. <div>
  262. <ElButton type="primary" class="w-full" @click="copyConfig">{{ t('setting.copy') }}</ElButton>
  263. </div>
  264. <div class="mt-5px">
  265. <ElButton type="danger" class="w-full" @click="clear">
  266. {{ t('setting.clearAndReset') }}
  267. </ElButton>
  268. </div>
  269. </ElDrawer>
  270. </template>
  271. <style lang="scss" scoped>
  272. $prefix-cls: #{$namespace}-setting;
  273. .#{$prefix-cls} {
  274. border-radius: 6px 0 0 6px;
  275. }
  276. </style>