Setting.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <script lang="ts" setup>
  2. import { ElMessage } from 'element-plus'
  3. import { useClipboard, useCssVar } from '@vueuse/core'
  4. import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
  5. import { useDesign } from '@/hooks/web/useDesign'
  6. import { setCssVar, trim } from '@/utils'
  7. import { colorIsDark, hexToRGB, lighten } from '@/utils/color'
  8. import { useAppStore } from '@/store/modules/app'
  9. import { ThemeSwitch } from '@/layout/components/ThemeSwitch'
  10. import ColorRadioPicker from './components/ColorRadioPicker.vue'
  11. import InterfaceDisplay from './components/InterfaceDisplay.vue'
  12. import LayoutRadioPicker from './components/LayoutRadioPicker.vue'
  13. defineOptions({ name: 'Setting' })
  14. const { t } = useI18n()
  15. const appStore = useAppStore()
  16. const { getPrefixCls } = useDesign()
  17. const prefixCls = getPrefixCls('setting')
  18. const layout = computed(() => appStore.getLayout)
  19. const drawer = ref(false)
  20. // 主题色相关
  21. const systemTheme = ref(appStore.getTheme.elColorPrimary)
  22. const setSystemTheme = (color: string) => {
  23. setCssVar('--el-color-primary', color)
  24. appStore.setTheme({ elColorPrimary: color })
  25. const leftMenuBgColor = useCssVar('--left-menu-bg-color', document.documentElement)
  26. setMenuTheme(trim(unref(leftMenuBgColor)))
  27. }
  28. // 头部主题相关
  29. const headerTheme = ref(appStore.getTheme.topHeaderBgColor || '')
  30. const setHeaderTheme = (color: string) => {
  31. const isDarkColor = colorIsDark(color)
  32. const textColor = isDarkColor ? '#fff' : 'inherit'
  33. const textHoverColor = isDarkColor ? lighten(color!, 6) : '#f6f6f6'
  34. const topToolBorderColor = isDarkColor ? color : '#eee'
  35. setCssVar('--top-header-bg-color', color)
  36. setCssVar('--top-header-text-color', textColor)
  37. setCssVar('--top-header-hover-color', textHoverColor)
  38. appStore.setTheme({
  39. topHeaderBgColor: color,
  40. topHeaderTextColor: textColor,
  41. topHeaderHoverColor: textHoverColor,
  42. topToolBorderColor
  43. })
  44. if (unref(layout) === 'top') {
  45. setMenuTheme(color)
  46. }
  47. }
  48. // 菜单主题相关
  49. const menuTheme = ref(appStore.getTheme.leftMenuBgColor || '')
  50. const setMenuTheme = (color: string) => {
  51. const primaryColor = useCssVar('--el-color-primary', document.documentElement)
  52. const isDarkColor = colorIsDark(color)
  53. const theme: Recordable = {
  54. // 左侧菜单边框颜色
  55. leftMenuBorderColor: isDarkColor ? 'inherit' : '#eee',
  56. // 左侧菜单背景颜色
  57. leftMenuBgColor: color,
  58. // 左侧菜单浅色背景颜色
  59. leftMenuBgLightColor: isDarkColor ? lighten(color!, 6) : color,
  60. // 左侧菜单选中背景颜色
  61. leftMenuBgActiveColor: isDarkColor
  62. ? 'var(--el-color-primary)'
  63. : hexToRGB(unref(primaryColor), 0.1),
  64. // 左侧菜单收起选中背景颜色
  65. leftMenuCollapseBgActiveColor: isDarkColor
  66. ? 'var(--el-color-primary)'
  67. : hexToRGB(unref(primaryColor), 0.1),
  68. // 左侧菜单字体颜色
  69. leftMenuTextColor: isDarkColor ? '#bfcbd9' : '#333',
  70. // 左侧菜单选中字体颜色
  71. leftMenuTextActiveColor: isDarkColor ? '#fff' : 'var(--el-color-primary)',
  72. // logo字体颜色
  73. logoTitleTextColor: isDarkColor ? '#fff' : 'inherit',
  74. // logo边框颜色
  75. logoBorderColor: isDarkColor ? color : '#eee'
  76. }
  77. appStore.setTheme(theme)
  78. appStore.setCssVarTheme()
  79. }
  80. if (layout.value === 'top' && !appStore.getIsDark) {
  81. headerTheme.value = '#fff'
  82. setHeaderTheme('#fff')
  83. }
  84. // 监听layout变化,重置一些主题色
  85. watch(
  86. () => layout.value,
  87. (n) => {
  88. if (n === 'top' && !appStore.getIsDark) {
  89. headerTheme.value = '#fff'
  90. setHeaderTheme('#fff')
  91. } else {
  92. setMenuTheme(unref(menuTheme))
  93. }
  94. }
  95. )
  96. // 拷贝
  97. const copyConfig = async () => {
  98. const { copy, copied, isSupported } = useClipboard({
  99. source: `
  100. // 面包屑
  101. breadcrumb: ${appStore.getBreadcrumb},
  102. // 面包屑图标
  103. breadcrumbIcon: ${appStore.getBreadcrumbIcon},
  104. // 折叠图标
  105. hamburger: ${appStore.getHamburger},
  106. // 全屏图标
  107. screenfull: ${appStore.getScreenfull},
  108. // 尺寸图标
  109. size: ${appStore.getSize},
  110. // 多语言图标
  111. locale: ${appStore.getLocale},
  112. // 消息图标
  113. message: ${appStore.getMessage},
  114. // 标签页
  115. tagsView: ${appStore.getTagsView},
  116. // 标签页
  117. tagsViewImmerse: ${appStore.getTagsViewImmerse},
  118. // 标签页图标
  119. tagsViewIcon: ${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 right-0 top-[45%] h-40px w-40px cursor-pointer bg-[var(--el-color-primary)] text-center leading-40px"
  191. @click="drawer = true"
  192. >
  193. <Icon color="#fff" icon="ep:setting" />
  194. </div>
  195. <ElDrawer v-model="drawer" :z-index="4000" direction="rtl" size="350px">
  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 class="w-full" type="primary" @click="copyConfig">{{ t('setting.copy') }}</ElButton>
  263. </div>
  264. <div class="mt-5px">
  265. <ElButton class="w-full" type="danger" @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. z-index: 1200;/*修正没有z-index会被表格层覆盖,值不要超过4000*/
  276. }
  277. </style>