feat(projects): 集成naiveUI主题配置,将css vars添加至html
This commit is contained in:
parent
0d2a5629e8
commit
2c196841bd
@ -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",
|
||||
|
@ -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
|
||||
|
17
src/App.vue
17
src/App.vue
@ -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>
|
||||
|
@ -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;
|
||||
}
|
@ -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 |
@ -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();
|
||||
|
40
src/store/modules/theme/helpers.ts
Normal file
40
src/store/modules/theme/helpers.ts
Normal 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;
|
||||
}
|
@ -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
116
src/utils/common/color.ts
Normal 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;
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
export * from './typeof';
|
||||
export * from './console';
|
||||
export * from './color';
|
||||
export * from './design-pattern';
|
||||
|
@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<div></div>
|
||||
<div>
|
||||
<h3 class="text-primary">Login</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
@ -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: []
|
||||
|
Loading…
Reference in New Issue
Block a user