refactor(projects): 嵌入naive-ui的css vars,替换windicss的extend color

This commit is contained in:
Soybean 2021-11-17 12:28:53 +08:00
parent 27f600c467
commit 2869b4cd33
13 changed files with 114 additions and 132 deletions

View File

@ -6,16 +6,18 @@
:theme="dark" :theme="dark"
:theme-overrides="theme.themeOverrids" :theme-overrides="theme.themeOverrids"
> >
<n-loading-bar-provider> <n-element class="h-full">
<n-dialog-provider> <n-loading-bar-provider>
<n-notification-provider> <n-dialog-provider>
<n-message-provider> <n-notification-provider>
<slot></slot> <n-message-provider>
<app-provider-content /> <slot></slot>
</n-message-provider> <app-provider-content />
</n-notification-provider> </n-message-provider>
</n-dialog-provider> </n-notification-provider>
</n-loading-bar-provider> </n-dialog-provider>
</n-loading-bar-provider>
</n-element>
</n-config-provider> </n-config-provider>
</template> </template>
@ -23,18 +25,18 @@
import { computed, watch } from 'vue'; import { computed, watch } from 'vue';
import { import {
NConfigProvider, NConfigProvider,
darkTheme, NElement,
zhCN,
dateZhCN,
NLoadingBarProvider, NLoadingBarProvider,
NDialogProvider, NDialogProvider,
NNotificationProvider, NNotificationProvider,
NMessageProvider NMessageProvider,
darkTheme,
zhCN,
dateZhCN
} from 'naive-ui'; } from 'naive-ui';
import { useDark } from '@vueuse/core'; import { useDark } from '@vueuse/core';
import { AppProviderContent } from '@/components'; import { AppProviderContent } from '@/components';
import { useThemeStore } from '@/store'; import { useThemeStore } from '@/store';
import { addColorAlpha } from '@/utils';
const osDark = useDark(); const osDark = useDark();
const theme = useThemeStore(); const theme = useThemeStore();
@ -43,13 +45,6 @@ const { handleDarkMode } = useThemeStore();
/** 系统暗黑模式 */ /** 系统暗黑模式 */
const dark = computed(() => (theme.darkMode ? darkTheme : undefined)); const dark = computed(() => (theme.darkMode ? darkTheme : undefined));
//
const primary = computed(() => theme.themeColor);
const primaryWithAlpha = computed(() => {
const alpha = theme.darkMode ? 0.15 : 0.1;
return addColorAlpha(primary.value, alpha);
});
// //
watch( watch(
osDark, osDark,
@ -61,18 +56,4 @@ watch(
} }
); );
</script> </script>
<style> <style></style>
/* 全局与主题颜色相关 */
.g_text-primary {
color: v-bind(primary);
}
.g_bg-primary {
background-color: v-bind(primary);
}
.g_bg-primary_active {
background-color: v-bind(primaryWithAlpha);
}
.g_border-primary {
border-color: v-bind(primary);
}
</style>

View File

@ -1,26 +1,17 @@
<template> <template>
<hover-container class="px-12px" :show-tooltip="false"> <hover-container class="px-12px" :show-tooltip="false">
<n-switch :value="theme.darkMode" size="large" @update:value="handleDarkMode"> <div class="hover:text-primary" @click="toggleDarkMode">
<template #checked> <icon-mdi-moon-waning-crescent v-if="theme.darkMode" class="text-14px" />
<icon-mdi-white-balance-sunny class="text-14px g_text-primary" /> <icon-mdi-white-balance-sunny v-else class="text-14px" />
</template> </div>
<template #unchecked>
<icon-mdi-moon-waning-crescent class="text-14px g_text-primary" />
</template>
</n-switch>
</hover-container> </hover-container>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { NSwitch } from 'naive-ui';
import { HoverContainer } from '@/components'; import { HoverContainer } from '@/components';
import { useThemeStore } from '@/store'; import { useThemeStore } from '@/store';
const theme = useThemeStore(); const theme = useThemeStore();
const { handleDarkMode } = useThemeStore(); const { toggleDarkMode } = useThemeStore();
</script> </script>
<style scoped> <style scoped></style>
:deep(.n-switch__rail) {
background-color: #000e1c !important;
}
</style>

View File

@ -2,7 +2,7 @@
<div class="mb-6px px-4px cursor-pointer" @mouseenter="setTrue" @mouseleave="setFalse"> <div class="mb-6px px-4px cursor-pointer" @mouseenter="setTrue" @mouseleave="setFalse">
<div <div
class="flex-center flex-col py-12px rounded-2px" class="flex-center flex-col py-12px rounded-2px"
:class="{ 'g_text-primary g_bg-primary_active': isActive, 'g_text-primary': isHover }" :class="{ 'text-primary bg-primary-active': isActive, 'text-primary': isHover }"
> >
<component :is="icon" :class="[isMini ? 'text-16px' : 'text-20px']" /> <component :is="icon" :class="[isMini ? 'text-16px' : 'text-20px']" />
<p <p

View File

@ -15,7 +15,7 @@
:style="{ width: showDrawer ? theme.menuStyle.width + 'px' : '0px' }" :style="{ width: showDrawer ? theme.menuStyle.width + 'px' : '0px' }"
> >
<header class="header-height flex-y-center justify-between"> <header class="header-height flex-y-center justify-between">
<h2 class="g_text-primary pl-8px text-16px font-bold">{{ title }}</h2> <h2 class="text-primary pl-8px text-16px font-bold">{{ title }}</h2>
<div class="px-8px text-16px text-gray-600 cursor-pointer" @click="toggleFixedMixMenu"> <div class="px-8px text-16px text-gray-600 cursor-pointer" @click="toggleFixedMixMenu">
<icon-mdi:pin-off v-if="app.menu.fixedMix" /> <icon-mdi:pin-off v-if="app.menu.fixedMix" />
<icon-mdi:pin v-else /> <icon-mdi:pin v-else />

View File

@ -3,10 +3,10 @@
<div class="flex-center"> <div class="flex-center">
<n-switch :value="theme.darkMode" @update:value="handleDarkMode"> <n-switch :value="theme.darkMode" @update:value="handleDarkMode">
<template #checked> <template #checked>
<icon-mdi-white-balance-sunny class="text-14px g_text-primary" /> <icon-mdi-white-balance-sunny class="text-14px text-primary" />
</template> </template>
<template #unchecked> <template #unchecked>
<icon-mdi-moon-waning-crescent class="text-14px g_text-primary" /> <icon-mdi-moon-waning-crescent class="text-14px text-primary" />
</template> </template>
</n-switch> </n-switch>
</div> </div>

View File

@ -1,7 +1,7 @@
<template> <template>
<a href="/" class="logo-height nowrap-hidden flex-center cursor-pointer"> <a href="/" class="logo-height nowrap-hidden flex-center cursor-pointer">
<system-logo class="w-32px h-32px" :color="primaryColor" /> <system-logo class="w-32px h-32px" :color="primaryColor" />
<h2 v-show="showTitle" class="g_text-primary pl-8px text-16px font-bold">{{ title }}</h2> <h2 v-show="showTitle" class="text-primary pl-8px text-16px font-bold">{{ title }}</h2>
</a> </a>
</template> </template>

View File

@ -1,8 +1,39 @@
import { brightenColor, darkenColor } from '@/utils'; import { brightenColor, darkenColor, addColorAlpha } from '@/utils';
export function getHoverAndPressedColor(color: string) { type ColorType = 'primary' | 'info' | 'success' | 'warning' | 'error';
return {
hover: brightenColor(color), type ColorScene = '' | 'Suppl' | 'Hover' | 'Pressed' | 'Active';
pressed: darkenColor(color)
}; 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 => brightenColor(color) },
{ scene: 'Pressed', handler: color => darkenColor(color) },
{ scene: 'Active', handler: color => addColorAlpha(color, 0.1) }
];
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

@ -3,17 +3,10 @@ import type { GlobalThemeOverrides } from 'naive-ui';
import { themeSettings, defaultThemeSettings } from '@/settings'; import { themeSettings, defaultThemeSettings } from '@/settings';
import { store } from '@/store'; import { store } from '@/store';
import type { ThemeSettings, NavMode, MultiTabMode, AnimateType, HorizontalMenuPosition } from '@/interface'; import type { ThemeSettings, NavMode, MultiTabMode, AnimateType, HorizontalMenuPosition } from '@/interface';
import { addColorAlpha } from '@/utils'; import { getThemeColors } from './helpers';
import { getHoverAndPressedColor } from './helpers';
type ThemeState = ThemeSettings; type ThemeState = ThemeSettings;
interface relativeThemeColor {
hover: string;
pressed: string;
shallow: string;
}
const themeStore = defineStore({ const themeStore = defineStore({
id: 'theme-store', id: 'theme-store',
state: (): ThemeState => ({ state: (): ThemeState => ({
@ -23,58 +16,29 @@ const themeStore = defineStore({
/** naive UI主题配置 */ /** naive UI主题配置 */
themeOverrids(): GlobalThemeOverrides { themeOverrids(): GlobalThemeOverrides {
const { const {
themeColor: primaryColor, themeColor,
otherColor: { info: infoColor, success: successColor, warning: warningColor, error: errorColor } otherColor: { info, success, warning, error }
} = this; } = this;
const { hover: primaryColorHover, pressed: primaryColorPressed } = getHoverAndPressedColor(primaryColor); const themeColors = getThemeColors([
const { hover: infoColorHover, pressed: infoColorPressed } = getHoverAndPressedColor(infoColor); ['primary', themeColor],
const { hover: successColorHover, pressed: successColorPressed } = getHoverAndPressedColor(successColor); ['info', info],
const { hover: warningColorHover, pressed: warningColorPressed } = getHoverAndPressedColor(warningColor); ['success', success],
const { hover: errorColorHover, pressed: errorColorPressed } = getHoverAndPressedColor(errorColor); ['warning', warning],
['error', error]
]);
const primaryColorSuppl = primaryColor; const colorLoading = themeColor;
const infoColorSuppl = infoColor;
const successColorSuppl = infoColor;
const warningColorSuppl = warningColor;
const errorColorSuppl = errorColor;
const colorLoading = primaryColor;
return { return {
common: { common: {
primaryColor, ...themeColors
primaryColorHover,
primaryColorPressed,
primaryColorSuppl,
infoColor,
infoColorHover,
infoColorPressed,
infoColorSuppl,
successColor,
successColorHover,
successColorPressed,
successColorSuppl,
warningColor,
warningColorHover,
warningColorPressed,
warningColorSuppl,
errorColor,
errorColorHover,
errorColorPressed,
errorColorSuppl
}, },
LoadingBar: { LoadingBar: {
colorLoading colorLoading
} }
}; };
}, },
relativeThemeColor(): relativeThemeColor {
const shallow = addColorAlpha(this.themeColor, 0.1);
return {
...getHoverAndPressedColor(this.themeColor),
shallow
};
},
isVerticalNav(): boolean { isVerticalNav(): boolean {
const { mode } = this.navStyle; const { mode } = this.navStyle;
return mode === 'vertical' || mode === 'vertical-mix'; return mode === 'vertical' || mode === 'vertical-mix';
@ -92,6 +56,10 @@ const themeStore = defineStore({
handleDarkMode(isDark: boolean) { handleDarkMode(isDark: boolean) {
this.darkMode = isDark; this.darkMode = isDark;
}, },
/** 切换暗黑模式 */
toggleDarkMode() {
this.darkMode = !this.darkMode;
},
/** 设置系统主题颜色 */ /** 设置系统主题颜色 */
setThemeColor(color: string) { setThemeColor(color: string) {
this.themeColor = color; this.themeColor = color;

View File

@ -8,10 +8,10 @@
<n-tag type="primary">{{ lastestBuildTime }}</n-tag> <n-tag type="primary">{{ lastestBuildTime }}</n-tag>
</n-descriptions-item> </n-descriptions-item>
<n-descriptions-item label="Github地址"> <n-descriptions-item label="Github地址">
<a class="g_text-primary" href="https://github.com/honghuangdc/soybean-admin" target="_blank">Github地址</a> <a class="text-primary" href="https://github.com/honghuangdc/soybean-admin" target="_blank">Github地址</a>
</n-descriptions-item> </n-descriptions-item>
<n-descriptions-item label="预览地址"> <n-descriptions-item label="预览地址">
<a class="g_text-primary" href="https://soybean.pro" target="_blank">预览地址</a> <a class="text-primary" href="https://soybean.pro" target="_blank">预览地址</a>
</n-descriptions-item> </n-descriptions-item>
</n-descriptions> </n-descriptions>
</n-card> </n-card>

View File

@ -4,7 +4,7 @@
<n-space :vertical="true" :size="16"> <n-space :vertical="true" :size="16">
<n-card title="项目主要技术栈" :bordered="false" size="small" class="shadow-sm rounded-16px"> <n-card title="项目主要技术栈" :bordered="false" size="small" class="shadow-sm rounded-16px">
<template #header-extra> <template #header-extra>
<a class="g_text-primary" href="javascript:;">更多技术栈</a> <a class="text-primary" href="javascript:;">更多技术栈</a>
</template> </template>
<n-grid :item-responsive="true" responsive="screen" cols="m:2 l:3" :x-gap="8" :y-gap="8"> <n-grid :item-responsive="true" responsive="screen" cols="m:2 l:3" :x-gap="8" :y-gap="8">
<n-grid-item v-for="item in technology" :key="item.id"> <n-grid-item v-for="item in technology" :key="item.id">
@ -14,7 +14,7 @@
</n-card> </n-card>
<n-card title="动态" :bordered="false" size="small" class="shadow-sm rounded-16px"> <n-card title="动态" :bordered="false" size="small" class="shadow-sm rounded-16px">
<template #header-extra> <template #header-extra>
<a class="g_text-primary" href="javascript:;">更多动态</a> <a class="text-primary" href="javascript:;">更多动态</a>
</template> </template>
<n-list> <n-list>
<n-list-item v-for="item in activity" :key="item.id"> <n-list-item v-for="item in activity" :key="item.id">

View File

@ -10,7 +10,7 @@
<n-space :vertical="true" size="large"> <n-space :vertical="true" size="large">
<div class="flex-y-center justify-between"> <div class="flex-y-center justify-between">
<n-checkbox v-model:checked="rememberMe">记住我</n-checkbox> <n-checkbox v-model:checked="rememberMe">记住我</n-checkbox>
<span class="g_text-primary cursor-pointer" @click="toCurrentLogin('reset-pwd')">忘记密码</span> <span class="text-primary cursor-pointer" @click="toCurrentLogin('reset-pwd')">忘记密码</span>
</div> </div>
<n-button type="primary" size="large" :block="true" :round="true" :loading="loading" @click="handleSubmit"> <n-button type="primary" size="large" :block="true" :round="true" :loading="loading" @click="handleSubmit">
确定 确定

View File

@ -10,7 +10,7 @@
</header> </header>
<main class="pt-24px"> <main class="pt-24px">
<div v-for="item in modules" v-show="module === item.key" :key="item.key"> <div v-for="item in modules" v-show="module === item.key" :key="item.key">
<h3 class="text-18px g_text-primary font-medium">{{ item.label }}</h3> <h3 class="text-18px text-primary font-medium">{{ item.label }}</h3>
<component :is="item.component" /> <component :is="item.component" />
</div> </div>
</main> </main>

View File

@ -1,9 +1,4 @@
import { defineConfig } from 'windicss/helpers'; import { defineConfig } from 'windicss/helpers';
import themeSettings from './src/settings/theme.json';
const {
otherColor: { info, success, warning, error }
} = themeSettings;
export default defineConfig({ export default defineConfig({
extract: { extract: {
@ -15,31 +10,47 @@ export default defineConfig({
'wh-full': 'w-full h-full', 'wh-full': 'w-full h-full',
'center-layout': 'w-1280px mx-auto px-15px', 'center-layout': 'w-1280px mx-auto px-15px',
'flex-center': 'flex justify-center items-center', 'flex-center': 'flex justify-center items-center',
'flex-col-center': 'flex flex-col justify-center items-center', 'flex-col-center': 'flex-center flex-col',
'flex-x-center': 'flex justify-center', 'flex-x-center': 'flex justify-center',
'flex-y-center': 'flex items-center', 'flex-y-center': 'flex items-center',
'inline-flex-center': 'inline-flex justify-center items-center', 'inline-flex-center': 'inline-flex justify-center items-center',
'inline-flex-x-center': 'inline-flex justify-center', 'inline-flex-x-center': 'inline-flex justify-center',
'inline-flex-y-center': 'inline-flex items-center', 'inline-flex-y-center': 'inline-flex items-center',
'flex-1-hidden': 'flex-1 overflow-hidden',
'flex-col-stretch': 'flex flex-col items-stretch', 'flex-col-stretch': 'flex flex-col items-stretch',
'inline-flex-col-stretch': 'flex flex-col items-stretch', 'inline-flex-col-stretch': 'flex flex-col items-stretch',
'absolute-center': 'absolute left-0 top-0 flex justify-center items-center wh-full', 'flex-1-hidden': 'flex-1 overflow-hidden',
'absolute-center': 'absolute left-0 top-0 flex-center wh-full',
'absolute-lt': 'absolute left-0 top-0', 'absolute-lt': 'absolute left-0 top-0',
'absolute-lb': 'absolute left-0 bottom-0', 'absolute-lb': 'absolute left-0 bottom-0',
'absolute-rt': 'absolute right-0 top-0', 'absolute-rt': 'absolute right-0 top-0',
'absolute-rb': 'absolute right-0 bottom-0', 'absolute-rb': 'absolute right-0 bottom-0',
'fixed-center': 'fixed left-0 top-0 flex justify-center items-center wh-full', 'fixed-center': 'fixed left-0 top-0 flex-center wh-full',
'ellipsis-text': 'whitespace-nowrap overflow-hidden overflow-ellipsis', 'nowrap-hidden': 'whitespace-nowrap overflow-hidden',
'nowrap-hidden': 'whitespace-nowrap overflow-hidden' 'ellipsis-text': 'nowrap-hidden overflow-ellipsis'
}, },
theme: { theme: {
extend: { extend: {
colors: { colors: {
info, primary: 'var(--primary-color)',
success, 'primary-hover': 'var(--primary-color-hover)',
warning, 'primary-pressed': 'var(--primary-color-pressed)',
error, '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)',
light: '#ffffff', light: '#ffffff',
dark: '#18181c' dark: '#18181c'
}, },