123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- <script setup lang="ts">
- import { ElDrawer, ElDivider, ElMessage } from 'element-plus'
- import { ref, unref, computed, watch } from 'vue'
- import { useI18n } from '@/hooks/web/useI18n'
- import { ThemeSwitch } from '@/components/ThemeSwitch'
- import { colorIsDark, lighten, hexToRGB } from '@/utils/color'
- import { useCssVar } from '@vueuse/core'
- import { useAppStore } from '@/store/modules/app'
- import { trim, setCssVar } from '@/utils'
- import ColorRadioPicker from './components/ColorRadioPicker.vue'
- import InterfaceDisplay from './components/InterfaceDisplay.vue'
- import LayoutRadioPicker from './components/LayoutRadioPicker.vue'
- import { useCache } from '@/hooks/web/useCache'
- import { useClipboard } from '@vueuse/core'
- import { useDesign } from '@/hooks/web/useDesign'
- const { getPrefixCls } = useDesign()
- const prefixCls = getPrefixCls('setting')
- const appStore = useAppStore()
- const { t } = useI18n()
- const layout = computed(() => appStore.getLayout)
- const drawer = ref(false)
- // 主题色相关
- const systemTheme = ref(appStore.getTheme.elColorPrimary)
- const setSystemTheme = (color: string) => {
- setCssVar('--el-color-primary', color)
- appStore.setTheme({ elColorPrimary: color })
- const leftMenuBgColor = useCssVar('--left-menu-bg-color', document.documentElement)
- setMenuTheme(trim(unref(leftMenuBgColor)))
- }
- // 头部主题相关
- const headerTheme = ref(appStore.getTheme.topHeaderBgColor || '')
- const setHeaderTheme = (color: string) => {
- const isDarkColor = colorIsDark(color)
- const textColor = isDarkColor ? '#fff' : 'inherit'
- const textHoverColor = isDarkColor ? lighten(color!, 6) : '#f6f6f6'
- const topToolBorderColor = isDarkColor ? color : '#eee'
- setCssVar('--top-header-bg-color', color)
- setCssVar('--top-header-text-color', textColor)
- setCssVar('--top-header-hover-color', textHoverColor)
- setCssVar('--top-tool-border-color', topToolBorderColor)
- appStore.setTheme({
- topHeaderBgColor: color,
- topHeaderTextColor: textColor,
- topHeaderHoverColor: textHoverColor,
- topToolBorderColor
- })
- if (unref(layout) === 'top') {
- setMenuTheme(color)
- }
- }
- // 菜单主题相关
- const menuTheme = ref(appStore.getTheme.leftMenuBgColor || '')
- const setMenuTheme = (color: string) => {
- const primaryColor = useCssVar('--el-color-primary', document.documentElement)
- const isDarkColor = colorIsDark(color)
- const theme: Recordable = {
- // 左侧菜单边框颜色
- leftMenuBorderColor: isDarkColor ? 'inherit' : '#eee',
- // 左侧菜单背景颜色
- leftMenuBgColor: color,
- // 左侧菜单浅色背景颜色
- leftMenuBgLightColor: isDarkColor ? lighten(color!, 6) : color,
- // 左侧菜单选中背景颜色
- leftMenuBgActiveColor: isDarkColor
- ? 'var(--el-color-primary)'
- : hexToRGB(unref(primaryColor), 0.1),
- // 左侧菜单收起选中背景颜色
- leftMenuCollapseBgActiveColor: isDarkColor
- ? 'var(--el-color-primary)'
- : hexToRGB(unref(primaryColor), 0.1),
- // 左侧菜单字体颜色
- leftMenuTextColor: isDarkColor ? '#bfcbd9' : '#333',
- // 左侧菜单选中字体颜色
- leftMenuTextActiveColor: isDarkColor ? '#fff' : 'var(--el-color-primary)',
- // logo字体颜色
- logoTitleTextColor: isDarkColor ? '#fff' : 'inherit',
- // logo边框颜色
- logoBorderColor: isDarkColor ? color : '#eee'
- }
- appStore.setTheme(theme)
- appStore.setCssVarTheme()
- }
- if (layout.value === 'top' && !appStore.getIsDark) {
- headerTheme.value = '#fff'
- setHeaderTheme('#fff')
- }
- // 监听layout变化,重置一些主题色
- watch(
- () => layout.value,
- (n) => {
- if (n === 'top' && !appStore.getIsDark) {
- headerTheme.value = '#fff'
- setHeaderTheme('#fff')
- } else {
- setMenuTheme(unref(menuTheme))
- }
- }
- )
- // 拷贝
- const copyConfig = async () => {
- const { copy, copied, isSupported } = useClipboard({
- source: `
- // 面包屑
- breadcrumb: ${appStore.getBreadcrumb},
- // 面包屑图标
- breadcrumbIcon: ${appStore.getBreadcrumbIcon},
- // 折叠图标
- hamburger: ${appStore.getHamburger},
- // 全屏图标
- screenfull: ${appStore.getScreenfull},
- // 尺寸图标
- size: ${appStore.getSize},
- // 多语言图标
- locale: ${appStore.getLocale},
- // 标签页
- tagsView: ${appStore.getTagsView},
- // 标签页图标
- getTagsViewIcon: ${appStore.getTagsViewIcon},
- // logo
- logo: ${appStore.getLogo},
- // 菜单手风琴
- uniqueOpened: ${appStore.getUniqueOpened},
- // 固定header
- fixedHeader: ${appStore.getFixedHeader},
- // 页脚
- footer: ${appStore.getFooter},
- // 灰色模式
- greyMode: ${appStore.getGreyMode},
- // layout布局
- layout: '${appStore.getLayout}',
- // 暗黑模式
- isDark: ${appStore.getIsDark},
- // 组件尺寸
- currentSize: '${appStore.getCurrentSize}',
- // 主题相关
- theme: {
- // 主题色
- elColorPrimary: '${appStore.getTheme.elColorPrimary}',
- // 左侧菜单边框颜色
- leftMenuBorderColor: '${appStore.getTheme.leftMenuBorderColor}',
- // 左侧菜单背景颜色
- leftMenuBgColor: '${appStore.getTheme.leftMenuBgColor}',
- // 左侧菜单浅色背景颜色
- leftMenuBgLightColor: '${appStore.getTheme.leftMenuBgLightColor}',
- // 左侧菜单选中背景颜色
- leftMenuBgActiveColor: '${appStore.getTheme.leftMenuBgActiveColor}',
- // 左侧菜单收起选中背景颜色
- leftMenuCollapseBgActiveColor: '${appStore.getTheme.leftMenuCollapseBgActiveColor}',
- // 左侧菜单字体颜色
- leftMenuTextColor: '${appStore.getTheme.leftMenuTextColor}',
- // 左侧菜单选中字体颜色
- leftMenuTextActiveColor: '${appStore.getTheme.leftMenuTextActiveColor}',
- // logo字体颜色
- logoTitleTextColor: '${appStore.getTheme.logoTitleTextColor}',
- // logo边框颜色
- logoBorderColor: '${appStore.getTheme.logoBorderColor}',
- // 头部背景颜色
- topHeaderBgColor: '${appStore.getTheme.topHeaderBgColor}',
- // 头部字体颜色
- topHeaderTextColor: '${appStore.getTheme.topHeaderTextColor}',
- // 头部悬停颜色
- topHeaderHoverColor: '${appStore.getTheme.topHeaderHoverColor}',
- // 头部边框颜色
- topToolBorderColor: '${appStore.getTheme.topToolBorderColor}'
- }
- `
- })
- if (!isSupported) {
- ElMessage.error(t('setting.copyFailed'))
- } else {
- await copy()
- if (unref(copied)) {
- ElMessage.success(t('setting.copySuccess'))
- }
- }
- }
- // 清空缓存
- const clear = () => {
- const { wsCache } = useCache()
- wsCache.delete('layout')
- wsCache.delete('theme')
- wsCache.delete('isDark')
- window.location.reload()
- }
- </script>
- <template>
- <div
- :class="prefixCls"
- class="fixed top-[45%] right-0 w-40px h-40px text-center leading-40px bg-[var(--el-color-primary)] cursor-pointer"
- @click="drawer = true"
- >
- <Icon icon="ep:setting" color="#fff" />
- </div>
- <ElDrawer v-model="drawer" direction="rtl" size="350px" :z-index="4000">
- <template #header>
- <span class="text-16px font-700">{{ t('setting.projectSetting') }}</span>
- </template>
- <div class="text-center">
- <!-- 主题 -->
- <ElDivider>{{ t('setting.theme') }}</ElDivider>
- <ThemeSwitch />
- <!-- 布局 -->
- <ElDivider>{{ t('setting.layout') }}</ElDivider>
- <LayoutRadioPicker />
- <!-- 系统主题 -->
- <ElDivider>{{ t('setting.systemTheme') }}</ElDivider>
- <ColorRadioPicker
- v-model="systemTheme"
- :schema="[
- '#409eff',
- '#009688',
- '#536dfe',
- '#ff5c93',
- '#ee4f12',
- '#0096c7',
- '#9c27b0',
- '#ff9800'
- ]"
- @change="setSystemTheme"
- />
- <!-- 头部主题 -->
- <ElDivider>{{ t('setting.headerTheme') }}</ElDivider>
- <ColorRadioPicker
- v-model="headerTheme"
- :schema="[
- '#fff',
- '#151515',
- '#5172dc',
- '#e74c3c',
- '#24292e',
- '#394664',
- '#009688',
- '#383f45'
- ]"
- @change="setHeaderTheme"
- />
- <!-- 菜单主题 -->
- <template v-if="layout !== 'top'">
- <ElDivider>{{ t('setting.menuTheme') }}</ElDivider>
- <ColorRadioPicker
- v-model="menuTheme"
- :schema="[
- '#fff',
- '#001529',
- '#212121',
- '#273352',
- '#191b24',
- '#383f45',
- '#001628',
- '#344058'
- ]"
- @change="setMenuTheme"
- />
- </template>
- </div>
- <!-- 界面显示 -->
- <ElDivider>{{ t('setting.interfaceDisplay') }}</ElDivider>
- <InterfaceDisplay />
- <ElDivider />
- <div>
- <ElButton type="primary" class="w-full" @click="copyConfig">{{ t('setting.copy') }}</ElButton>
- </div>
- <div class="mt-5px">
- <ElButton type="danger" class="w-full" @click="clear">
- {{ t('setting.clearAndReset') }}
- </ElButton>
- </div>
- </ElDrawer>
- </template>
- <style lang="less" scoped>
- @prefix-cls: ~'@{namespace}-setting';
- .@{prefix-cls} {
- border-radius: 6px 0 0 6px;
- }
- </style>
|