feat(projects): 集成naiveUI主题配置,将css vars添加至html

This commit is contained in:
Soybean 2022-01-04 02:20:32 +08:00
parent 0d2a5629e8
commit 2c196841bd
12 changed files with 297 additions and 88 deletions

View File

@ -23,9 +23,11 @@
"dependencies": {
"@vueuse/core": "^7.5.1",
"axios": "^0.24.0",
"colord": "^2.9.2",
"crypto-js": "^4.1.1",
"dayjs": "^1.10.7",
"form-data": "^4.0.0",
"lodash-es": "^4.17.21",
"naive-ui": "^2.23.2",
"pinia": "^2.0.9",
"qs": "^6.10.2",

View File

@ -15,6 +15,7 @@ specifiers:
'@vue/eslint-config-typescript': ^10.0.0
'@vueuse/core': ^7.5.1
axios: ^0.24.0
colord: ^2.9.2
commitizen: ^4.2.4
cross-env: ^7.0.3
crypto-js: ^4.1.1
@ -30,6 +31,7 @@ specifiers:
form-data: ^4.0.0
husky: ^7.0.4
lint-staged: ^12.1.4
lodash-es: ^4.17.21
mockjs: ^1.1.0
naive-ui: ^2.23.2
patch-package: ^6.4.7
@ -55,9 +57,11 @@ specifiers:
dependencies:
'@vueuse/core': registry.npmmirror.com/@vueuse/core/7.5.1_vue@3.2.26
axios: registry.npmmirror.com/axios/0.24.0
colord: registry.npmmirror.com/colord/2.9.2
crypto-js: registry.npmmirror.com/crypto-js/4.1.1
dayjs: registry.npmmirror.com/dayjs/1.10.7
form-data: registry.nlark.com/form-data/4.0.0
lodash-es: registry.npmmirror.com/lodash-es/4.17.21
naive-ui: registry.npmmirror.com/naive-ui/2.23.2_vue@3.2.26
pinia: registry.npmmirror.com/pinia/2.0.9_typescript@4.5.4+vue@3.2.26
qs: registry.npmmirror.com/qs/6.10.2
@ -4217,6 +4221,12 @@ packages:
dependencies:
color-name: registry.nlark.com/color-name/1.1.4
registry.npmmirror.com/colord/2.9.2:
resolution: {integrity: sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/colord/download/colord-2.9.2.tgz}
name: colord
version: 2.9.2
dev: false
registry.npmmirror.com/colorette/2.0.16:
resolution: {integrity: sha1-cTua+E/bAAE58EVGvUqT9ipQhdo=, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/colorette/download/colorette-2.0.16.tgz?cache=0&sync_timestamp=1633673609067&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fcolorette%2Fdownload%2Fcolorette-2.0.16.tgz}
name: colorette

View File

@ -1,6 +1,19 @@
<template>
<router-view />
<n-config-provider :theme-overrides="theme.naiveThemeOverrides">
<router-view />
</n-config-provider>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import { onMounted } from 'vue';
import { NConfigProvider } from 'naive-ui';
import { useThemeStore } from '@/store';
const theme = useThemeStore();
const { addThemeCssVarsToRoot } = useThemeStore();
onMounted(() => {
addThemeCssVarsToRoot();
});
</script>
<style scoped></style>

View File

@ -1,74 +0,0 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
position: relative;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition: color 0.5s, background-color 0.5s;
line-height: 1.6;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69" xmlns:v="https://vecta.io/nano"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

Before

Width:  |  Height:  |  Size: 308 B

View File

@ -4,10 +4,6 @@ import { setupRouter } from '@/router';
import { setupStore } from '@/store';
import App from './App.vue';
function setupPlugins() {
setupAssets();
}
async function setupApp() {
const app = createApp(App);
@ -21,5 +17,6 @@ async function setupApp() {
app.mount('#app');
}
setupPlugins();
setupAssets();
setupApp();

View File

@ -0,0 +1,40 @@
import { colord } from 'colord';
import { getColorPalette } from '@/utils';
type ColorType = 'primary' | 'info' | 'success' | 'warning' | 'error';
type ColorScene = '' | 'Suppl' | 'Hover' | 'Pressed' | 'Active';
type ColorKey = `${ColorType}Color${ColorScene}`;
type ThemeColor = {
[key in ColorKey]?: string;
};
interface ColorAction {
scene: ColorScene;
handler: (color: string) => string;
}
/** 获取主题颜色的各种场景对应的颜色 */
export function getThemeColors(colors: [ColorType, string][]) {
const colorActions: ColorAction[] = [
{ scene: '', handler: color => color },
{ scene: 'Suppl', handler: color => color },
{ scene: 'Hover', handler: color => getColorPalette(color, 5) },
{ scene: 'Pressed', handler: color => getColorPalette(color, 7) },
{ scene: 'Active', handler: color => colord(color).alpha(0.1).toHex() }
];
const themeColor: ThemeColor = {};
colors.forEach(color => {
colorActions.forEach(action => {
const [colorType, colorValue] = color;
const colorKey: ColorKey = `${colorType}Color${action.scene}`;
themeColor[colorKey] = action.handler(colorValue);
});
});
return themeColor;
}

View File

@ -1,7 +1,87 @@
import { ref, computed } from 'vue';
import type { Ref, ComputedRef } from 'vue';
import { defineStore } from 'pinia';
import { useThemeVars } from 'naive-ui';
import type { GlobalThemeOverrides } from 'naive-ui';
import { kebabCase } from 'lodash-es';
import { getColorPalette } from '@/utils';
import { getThemeColors } from './helpers';
// interface ThemeStore {
// primary: string;
// }
interface OtherColor {
/** 信息 */
info: string;
/** 成功 */
success: string;
/** 警告 */
warning: string;
/** 错误 */
error: string;
}
export const useThemeStore = defineStore('theme-store', () => {});
interface ThemeStore {
/** 主题颜色 */
themeColor: Ref<string>;
/** 其他颜色 */
otherColor: ComputedRef<OtherColor>;
/** naiveUI的主题配置 */
naiveThemeOverrides: ComputedRef<GlobalThemeOverrides>;
/** 添加css vars至html */
addThemeCssVarsToRoot(): void;
}
type ThemeVarsKeys = keyof Exclude<GlobalThemeOverrides['common'], undefined>;
export const useThemeStore = defineStore('theme-store', () => {
const themeVars = useThemeVars();
const themeColor = ref('#1890ff');
const otherColor = computed<OtherColor>(() => ({
info: getColorPalette(themeColor.value, 7),
success: '#52c41a',
warning: '#faad14',
error: '#f5222d'
}));
const naiveThemeOverrides = computed<GlobalThemeOverrides>(() => {
const { info, success, warning, error } = otherColor.value;
const themeColors = getThemeColors([
['primary', themeColor.value],
['info', info],
['success', success],
['warning', warning],
['error', error]
]);
const colorLoading = themeColor.value;
return {
common: {
...themeColors
},
LoadingBar: {
colorLoading
}
};
});
function addThemeCssVarsToRoot() {
const updatedThemeVars = { ...themeVars.value };
Object.assign(updatedThemeVars, naiveThemeOverrides.value.common);
const keys = Object.keys(updatedThemeVars) as ThemeVarsKeys[];
const style: string[] = [];
keys.forEach(key => {
style.push(`--${kebabCase(key)}: ${updatedThemeVars[key]}`);
});
const styleStr = style.join(';');
document.documentElement.style.cssText += styleStr;
}
const themeStore: ThemeStore = {
themeColor,
otherColor,
naiveThemeOverrides,
addThemeCssVarsToRoot
};
return themeStore;
});

116
src/utils/common/color.ts Normal file
View File

@ -0,0 +1,116 @@
import { colord } from 'colord';
import type { HsvColor } from 'colord';
type ColorIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
const hueStep = 2;
const saturationStep = 16;
const saturationStep2 = 5;
const brightnessStep1 = 5;
const brightnessStep2 = 15;
const lightColorCount = 5;
const darkColorCount = 4;
/**
* (6)
* @param color -
* @param index - (6)
* @description ant-design调色板算法中借鉴 https://github.com/ant-design/ant-design/blob/master/components/style/color/colorPalette.less
*/
export function getColorPalette(color: string, index: ColorIndex) {
if (index === 6) return color;
const isLight = index < 6;
const hsv = colord(color).toHsv();
const i = isLight ? lightColorCount + 1 - index : index - lightColorCount - 1;
const newHsv: HsvColor = {
h: getHue(hsv, i, isLight),
s: getSaturation(hsv, i, isLight),
v: getValue(hsv, i, isLight)
};
return colord(newHsv).toHex();
}
/**
*
* @param color -
*/
export function getAllColorPalette(color: string) {
const indexs: ColorIndex[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
return indexs.map(index => getColorPalette(color, index));
}
/**
*
* @param hsv - hsv格式颜色值
* @param i - 6
* @param isLight -
*/
function getHue(hsv: HsvColor, i: number, isLight: boolean) {
let hue: number;
if (hsv.h >= 60 && hsv.h <= 240) {
// 冷色调
// 减淡变亮 色相顺时针旋转 更暖
// 加深变暗 色相逆时针旋转 更冷
hue = isLight ? hsv.h - hueStep * i : hsv.h + hueStep * i;
} else {
// 暖色调
// 减淡变亮 色相逆时针旋转 更暖
// 加深变暗 色相顺时针旋转 更冷
hue = isLight ? hsv.h + hueStep * i : hsv.h - hueStep * i;
}
if (hue < 0) {
hue += 360;
} else if (hue >= 360) {
hue -= 360;
}
return hue;
}
/**
*
* @param hsv - hsv格式颜色值
* @param i - 6
* @param isLight -
*/
function getSaturation(hsv: HsvColor, i: number, isLight: boolean) {
let saturation: number;
if (isLight) {
saturation = hsv.s - saturationStep * i;
} else if (i === darkColorCount) {
saturation = hsv.s + saturationStep;
} else {
saturation = hsv.s + saturationStep2 * i;
}
if (saturation > 100) {
saturation = 100;
}
if (isLight && i === lightColorCount && saturation > 10) {
saturation = 10;
}
if (saturation < 6) {
saturation = 6;
}
return saturation;
}
/**
*
* @param hsv - hsv格式颜色值
* @param i - 6
* @param isLight -
*/
function getValue(hsv: HsvColor, i: number, isLight: boolean) {
let value: number;
if (isLight) {
value = hsv.v + brightnessStep1 * i;
} else {
value = hsv.v - brightnessStep2 * i;
}
if (value > 100) {
value = 100;
}
return value;
}

View File

@ -1,3 +1,4 @@
export * from './typeof';
export * from './console';
export * from './color';
export * from './design-pattern';

View File

@ -1,5 +1,7 @@
<template>
<div></div>
<div>
<h3 class="text-primary">Login</h3>
</div>
</template>
<script setup lang="ts"></script>

View File

@ -42,7 +42,30 @@ export default defineConfig({
'ellipsis-text': 'nowrap-hidden overflow-ellipsis'
},
theme: {
extend: {}
extend: {
colors: {
primary: 'var(--primary-color)',
'primary-hover': 'var(--primary-color-hover)',
'primary-pressed': 'var(--primary-color-pressed)',
'primary-active': 'var(--primary-color-active)',
info: 'var(--info-color)',
'info-hover': 'var(--info-color-hover)',
'info-pressed': 'var(--info-color-pressed)',
'info-active': 'var(--info-color-active)',
success: 'var(--success-color)',
'success-hover': 'var(--success-color-hover)',
'success-pressed': 'var(--success-color-pressed)',
'success-active': 'var(--success-color-active)',
warning: 'var(--warning-color)',
'warning-hover': 'var(--warning-color-hover)',
'warning-pressed': 'var(--warning-color-pressed)',
'warning-active': 'var(--warning-color-active)',
error: 'var(--error-color)',
'error-hover': 'var(--error-color-hover)',
'error-pressed': 'var(--error-color-pressed)',
'error-active': 'var(--error-color-active)'
}
}
},
variants: {},
plugins: []