根据配置改颜色

This commit is contained in:
齐斌 2025-09-18 15:10:44 +08:00
parent 271a8368e5
commit 9720ee005a
11 changed files with 808 additions and 27 deletions

View File

@ -14,7 +14,7 @@
width: 100px!important;
height: 100px!important;
border-radius: 50%;
background: var(--theme-primary, #BA4A8F);
background:black;
padding: 5px!important;
}

View File

@ -1,16 +1,24 @@
$theme-primary: #BA4A8F;
$theme-darker: #C1B2E5;
$theme-dark: #DFD7F2;
$basic-white: #FFFFFF;
$basic-black: #000000;
// 动态主题色配置
// 这些SCSS变量现在会从CSS变量中获取值支持动态主题切换
$text-disabled: #C3C3C3;
$text-basic: #939393;
$text-theme: #BA4A8F;
$theme-primary: var(--theme-primary, #BA4A8F);
$theme-darker: var(--theme-darker, #C1B2E5);
$theme-dark: var(--theme-dark, #DFD7F2);
$theme-success: var(--theme-success, #18a058);
$theme-warning: var(--theme-warning, #f0a020);
$theme-error: var(--theme-error, #d03050);
$theme-info: var(--theme-info, #2080f0);
$border-theme: #C1B2E5;
$basic-white: var(--basic-white, #FFFFFF);
$basic-black: var(--basic-black, #000000);
$input-bg: #777777;
$text-disabled: var(--text-disabled, #C3C3C3);
$text-basic: var(--text-basic, #939393);
$text-theme: var(--text-theme, #BA4A8F);
$btn-primary: #BA4A8F;
$btn-basic: #EEEAF7;
$border-theme: var(--border-theme, #C1B2E5);
$input-bg: var(--input-bg, #777777);
$btn-primary: var(--btn-primary, #BA4A8F);
$btn-basic: var(--btn-basic, #EEEAF7);

View File

@ -1,10 +1,15 @@
@import "color.scss";
// 定义CSS自定义属性CSS变量
// 注意这些默认值会被JavaScript动态覆盖
:root {
--theme-primary: #{$theme-primary};
--theme-darker: #{$theme-darker};
--theme-dark: #{$theme-dark};
--theme-success: #18a058;
--theme-warning: #f0a020;
--theme-error: #d03050;
--theme-info: #2080f0;
--basic-white: #{$basic-white};
--basic-black: #{$basic-black};
--text-theme: #{$text-theme};

View File

@ -0,0 +1,92 @@
<template>
<div class="theme-switch">
<n-select
v-model:value="selectedTheme"
:options="themeOptions"
@update:value="handleThemeChange"
placeholder="选择主题"
style="width: 200px"
>
<template #option="{ node, option }">
<div class="theme-option">
<div
class="theme-color-preview"
:style="{ backgroundColor: option.color }"
></div>
<span>{{ option.label }}</span>
</div>
</template>
</n-select>
<!-- 当前主题信息 -->
<div v-if="currentTheme" class="current-theme-info">
<small>当前主题: {{ currentTheme.config.name }}</small>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { NSelect } from 'naive-ui';
import { switchTheme, getCurrentTheme, getAllThemes, onThemeChange } from '@/utils/theme.js';
//
const selectedTheme = ref('');
const currentTheme = ref(null);
//
const themeOptions = computed(() => {
const themes = getAllThemes();
return Object.entries(themes).map(([key, config]) => ({
label: config.name,
value: key,
color: config.primary
}));
});
//
const handleThemeChange = async (themeKey) => {
if (themeKey) {
await switchTheme(themeKey);
}
};
//
onMounted(() => {
//
currentTheme.value = getCurrentTheme();
selectedTheme.value = currentTheme.value?.key || 'default';
//
onThemeChange((themeInfo) => {
currentTheme.value = getCurrentTheme();
selectedTheme.value = themeInfo.key;
});
});
</script>
<style scoped>
.theme-switch {
display: flex;
flex-direction: column;
gap: 8px;
}
.theme-option {
display: flex;
align-items: center;
gap: 8px;
}
.theme-color-preview {
width: 16px;
height: 16px;
border-radius: 50%;
border: 1px solid #e0e0e0;
}
.current-theme-info {
color: var(--theme-primary);
font-size: 12px;
}
</style>

View File

@ -1518,7 +1518,7 @@ const numWidth = computed(() => {
.x-upload-download-button:hover {
background-color: #e6fffb;
color: #13c2c2;
}
.x-upload-delete-button:hover {

177
src/config/theme/README.md Normal file
View File

@ -0,0 +1,177 @@
# 主题系统使用文档
## 概述
这个主题系统可以根据网站URL自动切换主题色支持多环境和多品牌的主题配置。
## 功能特性
- 🎨 基于URL自动切换主题
- 🔄 支持运行时动态切换主题
- 🎯 同时更新CSS变量和UI框架主题
- 📱 支持多环境配置(开发、测试、生产)
- 🛠️ 提供完整的工具函数和组件
## 配置文件
### 主题配置 (`src/config/theme/index.js`)
```javascript
export const themeConfigs = {
default: {
name: '体制外主题',
primary: '#BA4A8F',
// ... 其他颜色
},
main: {
name: '体制内主题',
primary: '#1890FF',
// ... 其他颜色
}
// ... 更多主题
};
```
### URL映射规则
```javascript
export const urlThemeMapping = {
'main': ['main.fontree.com', 'fontree-main.com'],
'default': ['fontree.com', 'www.fontree.com'],
'test': ['test.fontree.com', 'staging'],
'dev': ['localhost', '127.0.0.1']
};
```
## 使用方法
### 1. 自动主题切换
系统会在应用启动时自动检测URL并应用相应主题无需手动配置。
### 2. 手动切换主题
```javascript
import { switchTheme } from '@/utils/theme.js';
// 切换到指定主题
switchTheme('main'); // 切换到体制内主题
switchTheme('test'); // 切换到测试主题
```
### 3. 在Vue组件中使用
```vue
<template>
<div>
<p>当前主题: {{ currentTheme?.config.name }}</p>
<button @click="switchTheme('main')">切换到体制内主题</button>
</div>
</template>
<script setup>
import { useTheme } from '@/utils/theme.js';
const { currentTheme, switchTheme } = useTheme();
</script>
```
### 4. 使用主题切换组件
```vue
<template>
<ThemeSwitch />
</template>
<script setup>
import ThemeSwitch from '@/components/ThemeSwitch.vue';
</script>
```
### 5. 在CSS中使用主题变量
```css
.my-component {
background-color: var(--theme-primary);
color: var(--theme-success);
border: 1px solid var(--theme-darker);
}
```
### 6. 监听主题变化
```javascript
import { onThemeChange } from '@/utils/theme.js';
// 监听主题变化
const unsubscribe = onThemeChange((themeInfo) => {
console.log('主题已切换:', themeInfo.key, themeInfo.config);
});
// 取消监听
unsubscribe();
```
## CSS变量列表
系统会自动设置以下CSS变量
- `--theme-primary`: 主题主色
- `--theme-darker`: 主题深色
- `--theme-dark`: 主题更深色
- `--theme-success`: 成功色
- `--theme-warning`: 警告色
- `--theme-error`: 错误色
- `--theme-info`: 信息色
## 添加新主题
1. 在 `themeConfigs` 中添加新主题配置
2. 在 `urlThemeMapping` 中添加URL匹配规则
3. 重启应用即可生效
```javascript
// 添加新主题
export const themeConfigs = {
// ... 现有主题
custom: {
name: '自定义主题',
primary: '#FF6B35',
darker: '#FF8C69',
// ... 其他颜色
}
};
// 添加URL映射
export const urlThemeMapping = {
// ... 现有映射
'custom': ['custom.fontree.com', 'my-custom-domain.com']
};
```
## 调试和测试
在浏览器控制台中可以看到主题切换的日志信息:
```
当前URL: http://localhost:3000
当前hostname: localhost
匹配到主题: dev (开发环境主题)
CSS变量已更新: {primary: "#13C2C2", ...}
主题系统初始化完成: {...}
```
## 注意事项
1. 主题系统会在应用启动时自动初始化
2. URL匹配是模糊匹配会检查hostname和完整URL
3. 如果没有匹配到任何规则,会使用默认主题
4. 主题切换会同时更新CSS变量和UI框架配置
5. 所有主题变化都会触发 `themeChanged` 事件
## 扩展功能
- 可以添加主题持久化存储
- 可以添加主题预览功能
- 可以集成用户偏好设置
- 可以添加主题动画过渡效果

View File

@ -0,0 +1,98 @@
/**
* 动态颜色配置
* 统一管理所有颜色变量支持主题切换
*/
import { themeConfigs } from './index.js';
/**
* 获取当前主题的颜色配置
* @param {string} themeKey 主题键名
* @returns {Object} 颜色配置对象
*/
export function getThemeColors(themeKey = 'default') {
const theme = themeConfigs[themeKey] || themeConfigs.default;
return {
// 主题色系
'theme-primary': theme.primary,
'theme-darker': theme.darker,
'theme-dark': theme.dark,
'theme-success': theme.success,
'theme-warning': theme.warning,
'theme-error': theme.error,
'theme-info': theme.info,
// 基础色系
'basic-white': '#FFFFFF',
'basic-black': '#000000',
// 文本色系
'text-disabled': '#C3C3C3',
'text-basic': '#939393',
'text-theme': theme.primary,
// 边框色系
'border-theme': theme.darker,
// 输入框色系
'input-bg': '#777777',
// 按钮色系
'btn-primary': theme.primary,
'btn-basic': '#EEEAF7'
};
}
/**
* 生成CSS变量字符串
* @param {string} themeKey 主题键名
* @returns {string} CSS变量定义字符串
*/
export function generateCSSVariables(themeKey = 'default') {
const colors = getThemeColors(themeKey);
let cssVars = ':root {\n';
Object.entries(colors).forEach(([key, value]) => {
cssVars += ` --${key}: ${value};\n`;
});
cssVars += '}\n';
return cssVars;
}
/**
* 生成SCSS变量字符串
* @param {string} themeKey 主题键名
* @returns {string} SCSS变量定义字符串
*/
export function generateSCSSVariables(themeKey = 'default') {
const colors = getThemeColors(themeKey);
let scssVars = '// 动态生成的SCSS变量 - 请勿手动修改\n';
scssVars += `// 当前主题: ${themeConfigs[themeKey]?.name || '默认主题'}\n\n`;
Object.entries(colors).forEach(([key, value]) => {
const scssKey = key.replace(/-/g, '-'); // 保持连字符格式
scssVars += `$${scssKey}: ${value};\n`;
});
return scssVars;
}
/**
* 应用颜色到DOM
* @param {string} themeKey 主题键名
*/
export function applyColorsToDOM(themeKey = 'default') {
const colors = getThemeColors(themeKey);
const root = document.documentElement;
Object.entries(colors).forEach(([key, value]) => {
root.style.setProperty(`--${key}`, value);
});
console.log('颜色已应用到DOM:', colors);
}

225
src/config/theme/index.js Normal file
View File

@ -0,0 +1,225 @@
/**
* 基于URL的主题配置系统
* 根据网站URL自动切换主题色
*/
// 主题配置映射表
export const themeConfigs = {
// 默认主题(体制外)
default: {
name: '体制外主题',
primary: '#BA4A8F',
darker: '#C1B2E5',
dark: '#DFD7F2',
success: '#18a058',
warning: '#f0a020',
error: '#d03050',
info: '#2080f0'
},
// 体制内主题
main: {
name: '体制内主题',
primary: '#1890FF',
darker: '#40A9FF',
dark: '#91D5FF',
success: '#52c41a',
warning: '#faad14',
error: '#f5222d',
info: '#1890ff'
},
// 测试环境主题
test: {
name: '测试环境主题',
primary: '#722ED1',
darker: '#B37FEB',
dark: '#D3ADF7',
success: '#52c41a',
warning: '#fa8c16',
error: '#f5222d',
info: '#722ed1'
},
// 开发环境主题
dev: {
name: '开发环境主题',
primary: '#BA4A8F',
darker: '#BA4A8F',
dark: '#BA4A8F',
success: '#52c41a',
warning: '#faad14',
error: '#f5222d',
info: '#BA4A8F '
}
};
// URL匹配规则
export const urlThemeMapping = {
// 生产环境 - 体制内
'main': ['main.fontree.com', 'fontree-main.com', 'main-fontree'],
// 生产环境 - 体制外(默认)
'default': ['fontree.com', 'www.fontree.com', 'app.fontree.com'],
// 测试环境
'test': ['127.0.0.1', 'fontree-test.com', 'test-fontree', 'staging'],
// 开发环境
'dev': ['localhost', '127.2.0.1', 'dev.fontree.com', 'fontree-dev.com', 'dev-fontree']
};
/**
* 根据当前URL获取主题配置
* @returns {Object} 主题配置对象
*/
export function getThemeByUrl() {
const hostname = window.location.hostname;
const href = window.location.href;
console.log('当前URL:', href);
console.log('当前hostname:', hostname);
// 遍历URL映射规则
for (const [themeKey, patterns] of Object.entries(urlThemeMapping)) {
for (const pattern of patterns) {
if (hostname.includes(pattern) || href.includes(pattern)) {
console.log(`匹配到主题: ${themeKey} (${themeConfigs[themeKey].name})`);
return {
key: themeKey,
config: themeConfigs[themeKey]
};
}
}
}
// 默认返回体制外主题
console.log('使用默认主题');
return {
key: 'default',
config: themeConfigs.default
};
}
/**
* 应用主题到CSS变量
* @param {Object} themeConfig 主题配置
*/
export async function applyThemeToCSS(themeConfig) {
// 使用新的颜色配置系统
const { applyColorsToDOM } = await import('./colors.js');
// 根据主题配置找到对应的主题键名
const themeKey = Object.keys(themeConfigs).find(key =>
themeConfigs[key].primary === themeConfig.primary
) || 'default';
// 应用所有颜色到DOM
applyColorsToDOM(themeKey);
console.log('CSS变量已更新:', themeConfig);
}
/**
* 生成Naive UI主题配置
* @param {Object} themeConfig 主题配置
* @returns {Object} Naive UI主题覆盖配置
*/
export function generateNaiveUITheme(themeConfig) {
return {
common: {
primaryColor: themeConfig.primary,
primaryColorHover: themeConfig.darker,
primaryColorPressed: themeConfig.primary,
primaryColorSuppl: themeConfig.primary,
successColor: themeConfig.success,
warningColor: themeConfig.warning,
errorColor: themeConfig.error,
infoColor: themeConfig.info
},
Button: {
// Primary按钮的颜色配置
colorPrimary: themeConfig.primary,
colorHoverPrimary: themeConfig.darker,
colorPressedPrimary: themeConfig.primary,
colorFocusPrimary: themeConfig.primary,
// Tertiary按钮的颜色配置
textColor: themeConfig.primary,
textColorHover: themeConfig.darker,
textColorPressed: themeConfig.primary,
textColorFocus: themeConfig.primary,
textColorDisabled: '#c0c4cc',
// 边框颜色
borderPrimary: themeConfig.primary,
borderHoverPrimary: themeConfig.darker,
borderPressedPrimary: themeConfig.primary,
borderFocusPrimary: themeConfig.primary,
// 波纹效果颜色
rippleColorPrimary: `${themeConfig.primary}1a`
},
DataTable: {
thColor: themeConfig.primary,
thColorHover: themeConfig.darker,
thTextColor: '#fff',
itemColorActive: themeConfig.primary,
sorterIconColor: '#fff'
},
Input: {
borderHover: themeConfig.primary,
borderFocus: themeConfig.primary,
boxShadowFocus: `0 0 0 2px ${themeConfig.primary}1a`
},
Select: {
peers: {
InternalSelection: {
borderHover: themeConfig.primary,
borderFocus: themeConfig.primary,
boxShadowFocus: `0 0 0 2px ${themeConfig.primary}1a`
}
}
},
// 添加更多组件的主题配置
Checkbox: {
colorChecked: themeConfig.primary,
colorCheckedHover: themeConfig.darker,
colorCheckedPressed: themeConfig.primary
},
Radio: {
colorActive: themeConfig.primary,
colorActiveHover: themeConfig.darker
},
Switch: {
railColorActive: themeConfig.primary,
railColorActiveHover: themeConfig.darker
}
};
}
/**
* 初始化主题系统
*/
export async function initThemeSystem() {
const { key, config } = getThemeByUrl();
// 应用CSS变量
await applyThemeToCSS(config);
// 存储当前主题信息到全局
window.__CURRENT_THEME__ = {
key,
config,
naiveUITheme: generateNaiveUITheme(config)
};
// 触发自定义事件,通知其他组件主题已更新
window.dispatchEvent(new CustomEvent('themeChanged', {
detail: { key, config }
}));
console.log('主题系统初始化完成:', window.__CURRENT_THEME__);
return window.__CURRENT_THEME__;
}

View File

@ -2,17 +2,39 @@ import router from "@/router/index.js";
import { createApp } from "vue";
import App from "./App.vue";
import { registerPlugins } from "./plugins/index.js";
import { settingConfig } from "@/config/settings/index.js";
import { initThemeSystem } from "@/config/theme/index.js";
import store from "@/store/index.js";
// 异步初始化应用
async function initApp() {
window.process = {
env: {
VUE_APP_API_URL: import.meta.env.VITE_API_URL,
},
};
// 动态设置CSS变量值
document.documentElement.style.setProperty('--theme-primary', settingConfig.themeColor);
// 初始化主题系统(必须在创建应用之前)
const currentTheme = await initThemeSystem();
// 更新store中的主题配置
store.updateTheme(currentTheme.naiveUITheme);
// 监听主题变化事件
window.addEventListener('themeChanged', (event) => {
const { key, config } = event.detail;
console.log('主题已切换:', key, config);
// 更新store中的主题
if (window.__CURRENT_THEME__) {
store.updateTheme(window.__CURRENT_THEME__.naiveUITheme);
}
});
const app = createApp(App);
// 注册插件
registerPlugins(app);
app.mount("#app");
}
// 启动应用
initApp().catch(console.error);

View File

@ -1,10 +1,21 @@
import { reactive } from "vue";
import VisitedViewAction from "./modules/visited-view";
import { settingConfig } from "@/config/settings/index.js";
import { generateNaiveUITheme } from "@/config/theme/index.js";
// 获取初始主题色(如果主题系统还未初始化,使用默认值)
const getInitialTheme = () => {
if (window.__CURRENT_THEME__) {
return generateNaiveUITheme(window.__CURRENT_THEME__.config);
}
return null; // 将在main.js中初始化后更新
};
const primaryColor = settingConfig.themeColor;
const originState = {
isCollapse: false,
themeOverrides: {
themeOverrides: getInitialTheme() || {
// 默认主题配置(作为后备)
DataTable: {
sorterIconColor:'#fff',
thColorHover: primaryColor,
@ -48,6 +59,13 @@ const store = {
changeDevice(device) {
this.state.device = device;
},
// 更新主题
updateTheme(themeOverrides) {
this.state.themeOverrides = themeOverrides;
console.log('Store主题已更新:', themeOverrides);
},
...VisitedViewAction
};
export default store;

136
src/utils/theme.js Normal file
View File

@ -0,0 +1,136 @@
/**
* 主题工具函数
* 提供主题切换和管理的实用工具
*/
import { applyThemeToCSS, generateNaiveUITheme, themeConfigs } from "@/config/theme/index.js";
import store from "@/store/index.js";
/**
* 手动切换主题
* @param {string} themeKey 主题键名
*/
export async function switchTheme(themeKey) {
if (!themeConfigs[themeKey]) {
console.error('主题不存在:', themeKey);
return false;
}
const config = themeConfigs[themeKey];
// 应用CSS变量
await applyThemeToCSS(config);
// 生成并应用UI框架主题
const naiveUITheme = generateNaiveUITheme(config);
store.updateTheme(naiveUITheme);
// 更新全局主题信息
window.__CURRENT_THEME__ = {
key: themeKey,
config,
naiveUITheme
};
// 强制刷新页面样式(确保所有组件都能应用新主题)
document.documentElement.style.display = 'none';
setTimeout(() => {
document.documentElement.style.display = '';
}, 0);
// 触发主题变化事件
window.dispatchEvent(new CustomEvent('themeChanged', {
detail: { key: themeKey, config }
}));
console.log('手动切换主题成功:', themeKey, config);
console.log('应用的Naive UI主题:', naiveUITheme);
return true;
}
/**
* 获取当前主题信息
* @returns {Object} 当前主题信息
*/
export function getCurrentTheme() {
return window.__CURRENT_THEME__ || null;
}
/**
* 获取所有可用主题
* @returns {Object} 所有主题配置
*/
export function getAllThemes() {
return themeConfigs;
}
/**
* 根据颜色值获取对比色用于文本颜色
* @param {string} hexColor 十六进制颜色值
* @returns {string} 对比色黑色或白色
*/
export function getContrastColor(hexColor) {
// 移除#号
const hex = hexColor.replace('#', '');
// 转换为RGB
const r = parseInt(hex.substr(0, 2), 16);
const g = parseInt(hex.substr(2, 2), 16);
const b = parseInt(hex.substr(4, 2), 16);
// 计算亮度
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
// 返回对比色
return brightness > 128 ? '#000000' : '#ffffff';
}
/**
* 生成主题色的变体浅色深色等
* @param {string} baseColor 基础颜色
* @returns {Object} 颜色变体
*/
export function generateColorVariants(baseColor) {
// 这里可以实现颜色变体生成算法
// 简单示例:
return {
base: baseColor,
light: baseColor + '40', // 添加透明度
lighter: baseColor + '20',
dark: baseColor,
darker: baseColor
};
}
/**
* 监听主题变化
* @param {Function} callback 回调函数
* @returns {Function} 取消监听的函数
*/
export function onThemeChange(callback) {
const handler = (event) => {
callback(event.detail);
};
window.addEventListener('themeChanged', handler);
// 返回取消监听的函数
return () => {
window.removeEventListener('themeChanged', handler);
};
}
/**
* 创建主题切换的Vue组合式函数
* @returns {Object} 主题相关的响应式数据和方法
*/
export function useTheme() {
const currentTheme = getCurrentTheme();
return {
currentTheme,
switchTheme,
getAllThemes,
onThemeChange
};
}