Setting.vue 9.1 KB

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