mirror of
https://github.com/zclzone/vue-naive-admin.git
synced 2025-04-30 22:29:01 +08:00
feat: 增加设置主题色功能
This commit is contained in:
parent
621c34a1fb
commit
cf3c4b9020
87
index.html
87
index.html
@ -5,23 +5,90 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
<link rel="stylesheet" href="/resource/loading.css" />
|
||||
<style>
|
||||
.loading-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
.dark .loading-container {
|
||||
background-color: #232324;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.loading-container .loading {
|
||||
--speed-of-animation: 0.9s;
|
||||
--gap: 12px;
|
||||
--first-color: #4c86f9;
|
||||
--second-color: #49a84c;
|
||||
--third-color: #f6bb02;
|
||||
--fourth-color: #26a69a;
|
||||
--fifth-color: #2196f3;
|
||||
|
||||
margin: auto;
|
||||
width: 160px;
|
||||
height: 100px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--gap);
|
||||
}
|
||||
|
||||
.loading-container .loading span {
|
||||
width: 6px;
|
||||
height: 80px;
|
||||
background: var(--first-color);
|
||||
animation: scale var(--speed-of-animation) ease-in-out infinite;
|
||||
}
|
||||
|
||||
.loading-container .loading span:nth-child(2) {
|
||||
background: var(--second-color);
|
||||
animation-delay: -0.8s;
|
||||
}
|
||||
|
||||
.loading-container .loading span:nth-child(3) {
|
||||
background: var(--third-color);
|
||||
animation-delay: -0.7s;
|
||||
}
|
||||
|
||||
.loading-container .loading span:nth-child(4) {
|
||||
background: var(--fourth-color);
|
||||
animation-delay: -0.6s;
|
||||
}
|
||||
|
||||
.loading-container .loading span:nth-child(5) {
|
||||
background: var(--fifth-color);
|
||||
animation-delay: -0.5s;
|
||||
}
|
||||
|
||||
@keyframes scale {
|
||||
0%,
|
||||
40%,
|
||||
100% {
|
||||
transform: scaleY(0.25);
|
||||
}
|
||||
|
||||
20% {
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<title><%= title %></title>
|
||||
</head>
|
||||
<body class="dark:text-#e9e9e9 auto-bg">
|
||||
<div id="app">
|
||||
<!-- 白屏时的loading效果 -->
|
||||
<div class="loading-container">
|
||||
<div class="loading-spin__container">
|
||||
<div class="loading-spin">
|
||||
<div class="left-0 top-0 loading-spin-item"></div>
|
||||
<div class="left-0 bottom-0 loading-spin-item loading-delay-500"></div>
|
||||
<div class="right-0 top-0 loading-spin-item loading-delay-1000"></div>
|
||||
<div class="right-0 bottom-0 loading-spin-item loading-delay-1500"></div>
|
||||
</div>
|
||||
<div class="loading">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="loading-title"><%= title %></div>
|
||||
</div>
|
||||
<script src="/resource/loading.js"></script>
|
||||
</div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
@ -10,6 +10,8 @@
|
||||
"lint:fix": "eslint --fix --ext .js,.vue ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.0.2",
|
||||
"@arco-design/color": "^0.4.0",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"axios": "^1.6.8",
|
||||
"dayjs": "^1.11.10",
|
||||
|
5648
pnpm-lock.yaml
generated
5648
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,93 +0,0 @@
|
||||
/**********************************
|
||||
* @Author: Ronnie Zhang
|
||||
* @LastEditor: Ronnie Zhang
|
||||
* @LastEditTime: 2023/12/04 22:50:18
|
||||
* @Email: zclzone@outlook.com
|
||||
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
**********************************/
|
||||
|
||||
.loading-container {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.loading-spin__container {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
margin: 36px 0;
|
||||
}
|
||||
|
||||
.loading-spin {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
animation: loadingSpin 1s linear infinite;
|
||||
}
|
||||
|
||||
.left-0 {
|
||||
left: 0;
|
||||
}
|
||||
.right-0 {
|
||||
right: 0;
|
||||
}
|
||||
.top-0 {
|
||||
top: 0;
|
||||
}
|
||||
.bottom-0 {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.loading-spin-item {
|
||||
position: absolute;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
background-color: var(--primary-color);
|
||||
border-radius: 8px;
|
||||
-webkit-animation: loadingPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
animation: loadingPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
@keyframes loadingSpin {
|
||||
from {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loadingPulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-delay-500 {
|
||||
-webkit-animation-delay: 500ms;
|
||||
animation-delay: 500ms;
|
||||
}
|
||||
.loading-delay-1000 {
|
||||
-webkit-animation-delay: 1000ms;
|
||||
animation-delay: 1000ms;
|
||||
}
|
||||
.loading-delay-1500 {
|
||||
-webkit-animation-delay: 1500ms;
|
||||
animation-delay: 1500ms;
|
||||
}
|
||||
|
||||
.loading-title {
|
||||
font-size: 28px;
|
||||
font-weight: 500;
|
||||
color: var(--primary-color);
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
/**********************************
|
||||
* @Author: Ronnie Zhang
|
||||
* @LastEditor: Ronnie Zhang
|
||||
* @LastEditTime: 2023/12/04 22:50:27
|
||||
* @Email: zclzone@outlook.com
|
||||
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
**********************************/
|
||||
|
||||
function addThemeColorCssVars() {
|
||||
const key = '__THEME_COLOR__'
|
||||
const defaultColor = '#316c72'
|
||||
const themeColor = localStorage.getItem(key) || defaultColor
|
||||
const cssVars = `--primary-color: ${themeColor}`
|
||||
document.documentElement.style.cssText = cssVars
|
||||
}
|
||||
|
||||
addThemeColorCssVars()
|
||||
|
11
src/App.vue
11
src/App.vue
@ -29,8 +29,6 @@
|
||||
<script setup>
|
||||
import { zhCN, dateZhCN, darkTheme } from 'naive-ui'
|
||||
import { LayoutSetting } from '@/components'
|
||||
import { useCssVar } from '@vueuse/core'
|
||||
import { kebabCase } from 'lodash-es'
|
||||
import { useAppStore, useTabStore } from '@/store'
|
||||
|
||||
const layouts = new Map()
|
||||
@ -50,15 +48,6 @@ const Layout = computed(() => {
|
||||
return getLayout(route.meta?.layout || appStore.layout)
|
||||
})
|
||||
|
||||
function setupCssVar() {
|
||||
const common = appStore.naiveThemeOverrides?.common || {}
|
||||
for (const key in common) {
|
||||
useCssVar(`--${kebabCase(key)}`, document.documentElement).value = common[key] || ''
|
||||
if (key === 'primaryColor') window.localStorage.setItem('__THEME_COLOR__', common[key] || '')
|
||||
}
|
||||
}
|
||||
setupCssVar()
|
||||
|
||||
const tabStore = useTabStore()
|
||||
const keepAliveNames = computed(() => {
|
||||
return tabStore.tabs.filter((item) => item.keepAlive).map((item) => item.name)
|
||||
|
25
src/components/common/ThemeSetting.vue
Normal file
25
src/components/common/ThemeSetting.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<n-tooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<n-color-picker
|
||||
class="mr-16 h-40 w-80"
|
||||
:value="appStore.primaryColor"
|
||||
:swatches="primaryColors"
|
||||
:on-update:value="(v) => appStore.setPrimaryColor(v)"
|
||||
/>
|
||||
</template>
|
||||
设置主题色
|
||||
</n-tooltip>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getPresetColors } from '@arco-design/color'
|
||||
import { useAppStore } from '@/store'
|
||||
const appStore = useAppStore()
|
||||
|
||||
const primaryColors = Object.entries(getPresetColors()).map(([, value]) => value.primary)
|
||||
|
||||
watchEffect(() => {
|
||||
appStore.setThemeColor(appStore.primaryColor, appStore.isDark)
|
||||
})
|
||||
</script>
|
@ -66,7 +66,7 @@ function handleMenuSelect(key, item) {
|
||||
right: 8px;
|
||||
}
|
||||
&.n-menu-item-content--selected::before {
|
||||
border-left: 4px solid var(--primary-color);
|
||||
border-left: 4px solid rgb(var(--primary-color));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
<n-tabs
|
||||
:value="tabStore.activeTab"
|
||||
:closable="tabStore.tabs.length > 1"
|
||||
:style="`--selected-bg: ${appStore.isDark ? '#1b2429' : '#eaf0f1'}`"
|
||||
type="card"
|
||||
@close="(path) => tabStore.removeTab(path)"
|
||||
>
|
||||
@ -38,10 +37,9 @@
|
||||
|
||||
<script setup>
|
||||
import ContextMenu from './ContextMenu.vue'
|
||||
import { useTabStore, useAppStore } from '@/store'
|
||||
import { useTabStore } from '@/store'
|
||||
|
||||
const router = useRouter()
|
||||
const appStore = useAppStore()
|
||||
const tabStore = useTabStore()
|
||||
|
||||
const contextMenuOption = reactive({
|
||||
@ -85,12 +83,12 @@ async function handleContextMenu(e, tagItem) {
|
||||
border-radius: 4px !important;
|
||||
margin-right: 4px;
|
||||
&:hover {
|
||||
border: 1px solid var(--primary-color) !important;
|
||||
border: 1px solid rgb(var(--primary-color)) !important;
|
||||
}
|
||||
}
|
||||
.n-tabs-tab--active {
|
||||
border: 1px solid var(--primary-color) !important;
|
||||
background-color: var(--selected-bg) !important;
|
||||
border: 1px solid rgb(var(--primary-color)) !important;
|
||||
background-color: rgba(var(--primary-color), 0.1) !important;
|
||||
}
|
||||
.n-tabs-pad,
|
||||
.n-tabs-tab-pad,
|
||||
|
@ -32,6 +32,9 @@
|
||||
class="i-me:gitee mr-16 cursor-pointer"
|
||||
@click="handleLinkClick('https://gitee.com/isme-admin/vue-naive-admin/tree/2.x')"
|
||||
/>
|
||||
|
||||
<ThemeSetting class="mr-16" />
|
||||
|
||||
<UserAvatar />
|
||||
</div>
|
||||
</AppCard>
|
||||
|
@ -34,6 +34,9 @@
|
||||
class="i-me:gitee mr-16 cursor-pointer"
|
||||
@click="handleLinkClick('https://gitee.com/isme-admin/vue-naive-admin/tree/2.x')"
|
||||
/>
|
||||
|
||||
<ThemeSetting class="mr-16" />
|
||||
|
||||
<UserAvatar />
|
||||
</div>
|
||||
</AppCard>
|
||||
@ -56,4 +59,8 @@ const { isFullscreen, toggle } = useFullscreen()
|
||||
function handleLinkClick(link) {
|
||||
window.open(link)
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
appStore.setThemeColor(appStore.primaryColor, appStore.isDark)
|
||||
})
|
||||
</script>
|
||||
|
@ -8,32 +8,14 @@
|
||||
|
||||
export const defaultLayout = 'normal'
|
||||
|
||||
export const defaultPrimaryColor = '#316C72'
|
||||
|
||||
export const naiveThemeOverrides = {
|
||||
common: {
|
||||
primaryColor: '#316C72FF',
|
||||
primaryColorHover: '#316C72E3',
|
||||
primaryColorPressed: '#2B4C59FF',
|
||||
primaryColorSuppl: '#316C72E3',
|
||||
|
||||
infoColor: '#2080F0FF',
|
||||
infoColorHover: '#4098FCFF',
|
||||
infoColorPressed: '#1060C9FF',
|
||||
infoColorSuppl: '#4098FCFF',
|
||||
|
||||
successColor: '#18A058FF',
|
||||
successColorHover: '#36AD6AFF',
|
||||
successColorPressed: '#0C7A43FF',
|
||||
successColorSuppl: '#36AD6AFF',
|
||||
|
||||
warningColor: '#F0A020FF',
|
||||
warningColorHover: '#FCB040FF',
|
||||
warningColorPressed: '#C97C10FF',
|
||||
warningColorSuppl: '#FCB040FF',
|
||||
|
||||
errorColor: '#D03050FF',
|
||||
errorColorHover: '#DE576DFF',
|
||||
errorColorPressed: '#AB1F3FFF',
|
||||
errorColorSuppl: '#DE576DFF',
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -8,13 +8,15 @@
|
||||
|
||||
import { defineStore } from 'pinia'
|
||||
import { useDark } from '@vueuse/core'
|
||||
import { defaultLayout, naiveThemeOverrides } from '@/settings'
|
||||
import { generate, getRgbStr } from '@arco-design/color'
|
||||
import { defaultLayout, defaultPrimaryColor, naiveThemeOverrides } from '@/settings'
|
||||
|
||||
export const useAppStore = defineStore('app', {
|
||||
state: () => ({
|
||||
collapsed: false,
|
||||
isDark: useDark(),
|
||||
layout: defaultLayout,
|
||||
primaryColor: defaultPrimaryColor,
|
||||
naiveThemeOverrides,
|
||||
}),
|
||||
actions: {
|
||||
@ -30,9 +32,25 @@ export const useAppStore = defineStore('app', {
|
||||
setLayout(v) {
|
||||
this.layout = v
|
||||
},
|
||||
setPrimaryColor(color) {
|
||||
this.primaryColor = color
|
||||
},
|
||||
setThemeColor(color = this.primaryColor, isDark = this.isDark) {
|
||||
const colors = generate(color, {
|
||||
list: true,
|
||||
dark: isDark,
|
||||
})
|
||||
document.body.style.setProperty('--primary-color', getRgbStr(colors[5]))
|
||||
this.naiveThemeOverrides.common = Object.assign(this.naiveThemeOverrides.common || {}, {
|
||||
primaryColor: colors[5],
|
||||
primaryColorHover: colors[4],
|
||||
primaryColorSuppl: colors[4],
|
||||
primaryColorPressed: colors[6],
|
||||
})
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
paths: ['collapsed', 'naiveThemeOverrides'],
|
||||
paths: ['collapsed', 'layout', 'primaryColor', 'naiveThemeOverrides'],
|
||||
storage: sessionStorage,
|
||||
},
|
||||
})
|
||||
|
@ -68,7 +68,7 @@ body {
|
||||
background: #bfbfbf;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--primary-color);
|
||||
background: rgb(var(--primary-color));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,26 +48,7 @@ export default defineConfig({
|
||||
],
|
||||
theme: {
|
||||
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)',
|
||||
primary: 'rgba(var(--primary-color))',
|
||||
dark: '#18181c',
|
||||
light_border: '#efeff5',
|
||||
dark_border: '#2d2d30',
|
||||
|
Loading…
x
Reference in New Issue
Block a user