28 实现多主题切换 - udo-bit/naive_admin_pro GitHub Wiki

目标

完成基础的多主题的功能切换

开发

打开config/layout-theme.ts文件,添加一个新的属性theme,如下:

export interface LayoutTheme {
  theme?: string
}

接下来我们在config中创建一个theme的文件用于存放我们的样式。这里的主题色,我们采用的是ant.design提供的主题色,如果大家需要配置更多可以参考antd

import type { GlobalThemeOverrides } from 'naive-ui'
export const colors: Record<string, GlobalThemeOverrides> = {
  default: {
    common: {
        // 主色
      primaryColor: '#18a058',
      // hover
      primaryColorHover: '#36ad6a',
      // 按下的颜色
      primaryColorPressed: '#0c7a43',
      // 反色激活颜色
      primaryColorSuppl: '#36ad6a',
    },
  },
  dustRed: {
    common: {
      primaryColor: '#f5222d',
      primaryColorHover: '#ff4d4f',
      primaryColorPressed: '#cf1322',
      primaryColorSuppl: '#ff4d4f',
    },
  },
  volcano: {
    common: {
      primaryColor: '#fa541c',
      primaryColorHover: '#ff7a45',
      primaryColorPressed: '#d4380d',
      primaryColorSuppl: '#ff7a45',
    },
  },
  orange: {
    common: {
      primaryColor: '#fa8c16',
      primaryColorHover: '#ffa940',
      primaryColorPressed: '#d46b08',
      primaryColorSuppl: '#ffa940',
    },
  },
  cyan: {
    common: {
      primaryColor: '#13c2c2',
      primaryColorHover: '#36cfc9',
      primaryColorPressed: '#08979c',
      primaryColorSuppl: '#36cfc9',
    },
  },
  blue: {
    common: {
      primaryColor: '#1890ff',
      primaryColorHover: '#40a9ff',
      primaryColorPressed: '#096dd9',
      primaryColorSuppl: '#40a9ff',
    },
  },
  purple: {
    common: {
      primaryColor: '#722ed1',
      primaryColorHover: '#9254de',
      primaryColorPressed: '#531dab',
      primaryColorSuppl: '#9254de',
    },
  },
  magenta: {
    common: {
      primaryColor: '#eb2f96',
      primaryColorHover: '#f759ab',
      primaryColorPressed: '#c41d7f',
      primaryColorSuppl: '#f759ab',
    },
  },
}

export const darkColors: Record<string, GlobalThemeOverrides> = {
  default: {
    common: {
      primaryColor: '#18a058',
      primaryColorHover: '#7fe7c4',
      primaryColorPressed: '#5acea7',
      primaryColorSuppl: 'rgb(42,148,125)',
    },
  },
  dustRed: {
    common: {
      primaryColor: '#d32029', // 6
      primaryColorHover: '#e84749', // 7
      primaryColorPressed: '#f37370', // 8
      primaryColorSuppl: '#a61d24', // 5
    },
  },
  volcano: {
    common: {
      primaryColor: '#d84a1b',
      primaryColorHover: '#e87040',
      primaryColorPressed: '#f3956a',
      primaryColorSuppl: '#aa3e19',
    },
  },
  orange: {
    common: {
      primaryColor: '#d87a16',
      primaryColorHover: '#e89a3c',
      primaryColorPressed: '#f3b765',
      primaryColorSuppl: '#aa6215',
    },
  },
  cyan: {
    common: {
      primaryColor: '#13a8a8',
      primaryColorHover: '#33bcb7',
      primaryColorPressed: '#58d1c9',
      primaryColorSuppl: '#138585',
    },
  },
  blue: {
    common: {
      primaryColor: '#177ddc',
      primaryColorHover: '#3c9ae8',
      primaryColorPressed: '#65b7f3',
      primaryColorSuppl: '#1765ad',
    },
  },
  purple: {
    common: {
      primaryColor: '#642ab5',
      primaryColorHover: '#854eca',
      primaryColorPressed: '#ab7ae0',
      primaryColorSuppl: '#51258f',
    },
  },
  magenta: {
    common: {
      primaryColor: '#cb2b83',
      primaryColorHover: '#e0529c',
      primaryColorPressed: '#f37fb7',
      primaryColorSuppl: '#a02669',
    },
  },
}

切换到stores/app.ts中增加覆盖样式的代码如下:

  const overridesTheme = computed(() => {
    if (isDark.value)
      return darkColors[layout.theme]

    else
      return colors[layout.theme]
  })

  const updateTheme = (val: string) => {
    layout.theme = val
  }

  return {
    overridesTheme,
    updateTheme,
  }

最后我们在app.vue中增加覆盖主题的功能:

<script setup lang="ts">
const appStore = useAppStore()
const { layoutTheme, overridesTheme } = storeToRefs(appStore)
useAutoDark()
</script>

<template>
  <n-config-provider :theme="layoutTheme" :theme-overrides="overridesTheme">
    <app-provider>
      <router-view />
    </app-provider>
  </n-config-provider>
</template>

接下来我们在pages/index.vue中进行测试:

<script lang="ts" setup>
import { colors } from '~/config/theme'
const appStore = useAppStore()
const onSwitchTheme = (value: string) => {
  appStore.updateTheme(value)
}
</script>

<template>
  <div>
    <n-space>
      <n-button v-for="(color, key) in colors" :key="key" type="primary" @click="onSwitchTheme(key)">
        {{ key }}
      </n-button>
    </n-space>
  </div>
</template>

<style scoped>

</style>

当我们切换的时候,会发现我们的按钮的颜色也会跟着变化,那么我们的主题色切换就生效了。

实现侧边栏主题切换

我们在layouts/setting-drawer中增加一个checkbox-colors的文件。如下:

<script lang="ts" setup>
import { CheckOutlined } from '@vicons/antd'
defineProps<{
  color: string
  checked?: boolean
}>()
</script>

<template>
  <div :style="{ background: color }" class="cursor-pointer flex w-20px h-20px items-center justify-center">
    <n-icon v-if="checked" size="16">
      <CheckOutlined />
    </n-icon>
  </div>
</template>

在config/theme.ts中定义类型:

export interface ThemeType {
  color: string
  key: string
}

我们在stores/app.ts中将我们的样式实现:

const themeList = computed<ThemeType[]>(() => {
  const list: ThemeType[] = []
  const myColors = isDark.value ? darkColors : colors
  for (const colorsKey in myColors) {
    const value = myColors[colorsKey]
    list.push({
      color: value.common?.primaryColor as string,
      key: colorsKey,
    })
  }
  return list
})

然后再setting-drawer/index.vue中使用:

<script lang="ts" setup>
  import CheckboxColor from './checkbox-color.vue'
  import type { ThemeType } from '~/config/theme'
  const props = withDefaults(defineProps<{
  themeList?: ThemeType[]
  theme?: string
}>(), {
})
const emit = defineEmits(['update:theme'])
const onChangeTheme = (item: ThemeType) => {
  emit('update:theme', item.key)
}
</script>

<template>
  <Container v-if="themeList" title="主题色配置">
    <n-space>
      <CheckboxColor
        v-for="item in themeList"
        :key="item.key"
        :color="item.color"
        :checked="item.key === theme"
        @click="onChangeTheme(item)"
      />
    </n-space>
  </Container>
  <n-divider />
</template>

然后在base-layout中:

<script lang="ts" setup>
  const { layout, visible, layoutList, layoutStyleList, themeList } = storeToRefs(appStore)
</script>

<template>
  <SettingDrawer
    v-model:layout-style="layout.layoutStyle"
    v-model:layout="layout.layout"
    v-model:theme="layout.theme"
    :theme-list="themeList"
    :layout-list="layoutList"
    :layout-style-list="layoutStyleList"
  />
</template>

测试效果

我们发现暗黑模式下,在mac中我们上划和下滑的时候,整体的背景色还是不对的,所以我们在app.vue中添加如下导入:

<template>
  <n-config-provider :theme="layoutTheme" :theme-overrides="overridesTheme">
+    <n-global-style/>
    <app-provider>
      <router-view />
    </app-provider>
  </n-config-provider>
</template>

这样我们的背景就跟着主题色进行变化了。

⚠️ **GitHub.com Fallback** ⚠️