GoWind 开源生态GoWind 开源生态
首页
框架
GoWind Admin
GoWind CMS
GoWind IM
GoWind UBA
GoWind IoT
GoWind Toolkit
GoWind Quant
GitHub
首页
框架
GoWind Admin
GoWind CMS
GoWind IM
GoWind UBA
GoWind IoT
GoWind Toolkit
GoWind Quant
GitHub
  • 介绍

    • GoWind Admin 产品介绍
    • GoWind Admin 安装指南
  • 后端文档

    • 后端架构总览
    • 后端核心模块详解
    • 后端 API 与 Protobuf 定义
    • 后端配置与部署
    • 后端扩展开发
  • 前端文档

    • 前端架构总览
    • 前端核心功能详解
  • 二开教程

    • 后端新增业务模块实战教程
    • 前端新增业务页面实战教程
    • 前后端联调完整实战教程
  • 高级教程

    • Lua 脚本扩展实战教程
    • 权限系统深度解析教程
    • 多租户架构实战教程
    • 任务调度与异步处理教程
    • 文件上传与对象存储教程
    • 事件总线与解耦架构教程
    • 前端主题定制与国际化教程
    • 性能优化与监控教程
    • SSE 实时推送
    • 登录策略与安全加固
    • 加密工具实战

前端主题定制与国际化教程

GoWind Admin 提供三套前端实现(Vue Vben、Vue Element、React),三套前端均支持完整的主题定制和多语言国际化能力。本教程分别讲解三套前端的主题系统和国际化机制。

一、三套前端主题与国际化概览

1.1 技术栈对比

特性Vue Vben 版Vue Element 版React 版
UI 框架Ant Design VueElement PlusAnt Design V6
主题引擎Vben Admin PreferencesCSS Variables + 自定义引擎Ant Design Theme + Zustand
暗黑模式light / dark / autolight / dark / autolight / dark / auto
内置主题Vben Admin 内置15+ 内置预设色15+ 内置预设色
CSS 方案Tailwind CSS + UnoCSSUnoCSSTailwind CSS
i18n 库vue-i18n(@vben/locales)vue-i18ni18next + react-i18next
支持语言zh-CN / en-USzh-CN / en-USzh-CN / en-US
语言包格式JSON(按语言分目录)JSON(按语言分目录)JSON(按命名空间分模块)
状态管理PiniaVue ReactiveZustand
项目路径frontend/admin/vue-vbenfrontend/admin/vue-elementfrontend/admin/react

1.2 共同设计理念

三套前端共享以下设计理念:

  • CSS Variables 驱动:通过 CSS 变量实现运行时动态主题切换
  • Preferences 偏好系统:统一管理主题、布局、语言等偏好设置
  • localStorage 持久化:用户偏好自动保存到本地存储
  • 系统主题跟随:支持 auto 模式自动跟随操作系统暗黑模式

二、Vue Vben 版 — 主题定制

2.1 主题架构

Vue Vben 版基于 Vben Admin 框架的主题系统,核心位于 packages/@core/ 共享包中:

packages/@core/
├── base/shared/src/color.ts     # 主题颜色工具
└── preferences/                  # 偏好设置系统

apps/admin/src/
├── preferences.ts                # 主题偏好覆盖配置
└── locales/                      # 国际化配置

2.2 修改主色调

在 apps/admin/src/preferences.ts 中覆盖主题配置:

import { defineOverridesPreferences } from '@vben/preferences';

export const overridesPreferences = defineOverridesPreferences({
  theme: {
    colorPrimary: '#722ed1',  // 紫色主题
  },
});

2.3 完整主题配置

export const overridesPreferences = defineOverridesPreferences({
  theme: {
    // 主色
    colorPrimary: '#722ed1',

    // 主题模式:light | dark | auto
    mode: 'light',

    // 圆角大小
    borderRadius: 6,

    // 是否开启紧凑模式
    compact: false,
  },

  // 布局配置
  layout: {
    // 导航模式:horizontal(顶部)| vertical(左侧)
    mode: 'vertical',
    tabbar: true,
    breadcrumb: true,
  },
});

2.4 运行时切换主题

<script setup>
import { useThemeStore } from '@vben/stores';

const themeStore = useThemeStore();

// 切换主色
function changePrimaryColor(color) {
  themeStore.updatePreferences({
    theme: { colorPrimary: color },
  });
}

// 切换暗黑模式
function toggleDarkMode() {
  themeStore.updatePreferences({
    theme: {
      mode: themeStore.isDark ? 'light' : 'dark',
    },
  });
}
</script>

<template>
  <div>
    <Button @click="changePrimaryColor('#1890ff')">蓝色主题</Button>
    <Button @click="changePrimaryColor('#722ed1')">紫色主题</Button>
    <Button @click="toggleDarkMode">切换暗黑模式</Button>
  </div>
</template>

2.5 暗黑模式适配

自定义暗黑模式样式,在 apps/admin/src/styles/ 中定义:

html.dark {
  --bg-color: #141414;
  --bg-color-secondary: #1f1f1f;
  --text-color: rgba(255, 255, 255, 0.85);
  --border-color: #303030;
}

html.dark .custom-card {
  background: linear-gradient(145deg, #1f1f1f, #2a2a2a);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

三、Vue Element 版 — 主题定制

3.1 主题架构

Vue Element 版拥有完全自研的主题引擎,核心位于 src/core/preferences/ 和 src/utils/theme.ts:

src/core/preferences/
├── config/
│   ├── constants.ts          # 内置主题预设(15+ 种预设色)
│   └── default.ts            # 默认偏好配置
├── types/
│   └── theme.ts              # 主题类型定义
├── update-css-variables.ts   # CSS 变量更新引擎
├── preferences.ts            # 偏好管理器
└── use-preferences.ts        # 偏好组合式函数

src/utils/
├── color.ts                  # 颜色转换工具(Hex/RGB/HSL)
└── theme.ts                  # 主题工具函数

3.2 内置主题预设

Vue Element 版提供 15+ 种内置主题预设色:

预设名说明预设名说明
default默认蓝色deep-blue深蓝色
green绿色deep-green深绿色
red红色rose玫红色
orange橙色yellow黄色
pink粉色violet紫罗兰色
sky-blue天蓝色gray灰色
slate石板灰色stone石灰色
neutral中性色zinc锌色
custom自定义主题

3.3 修改默认主题

在 src/core/preferences/config/default.ts 中修改:

const defaultPreferences: Preferences = {
  // ... 其他配置
  theme: {
    builtinType: "default",          // 内置主题类型
    colorPrimary: "hsl(220 100% 55%)",   // 主色(支持 HSL)
    colorSuccess: "hsl(145 100% 35%)",   // 成功色
    colorWarning: "hsl(32 100% 50%)",    // 警告色
    colorDestructive: "hsl(0 91% 60%)",  // 危险色
    mode: "dark",                          // light | dark | auto
    radius: "0.5",                         // 圆角(rem)
    semiDarkSidebar: false,                // 半暗侧边栏
    semiDarkHeader: false,                 // 半暗顶栏
  },
};

3.4 CSS 变量更新机制

Vue Element 版的主题引擎通过 update-css-variables.ts 实现运行时动态主题切换:

// 主题更新流程
// 1. preferences 变更 → handleUpdates() 检测到 theme 变化
// 2. 调用 updateCSSVariables() 生成 CSS 变量
// 3. 生成两套变量:
//    - Element Plus 变量:--el-color-primary, --el-color-primary-light-1 等
//    - 自定义变量:--primary-500, --success-500 等(Tailwind 风格)
// 4. 同时更新 vxe-table 暗色主题

// 关键函数
import { generateColorVariables, generatorColorVariables } from "@/utils/theme";

// 生成 Element Plus 颜色变量
const epVariables = generateColorVariables({
  colorPrimary: "#4080FF",
  colorSuccess: "#52c41a",
  colorWarning: "#faad14",
  colorDestructive: "#ff4d4f",
  mode: "light",
});
// 输出: { "--el-color-primary": "#4080FF", "--el-color-primary-light-1": "...", ... }

// 生成自定义 Tailwind 风格变量
const customVariables = generatorColorVariables([
  { color: "#4080FF", name: "primary" },
  { color: "#52c41a", name: "green", alias: "success" },
]);
// 输出: { "--primary-500": "#4080FF", "--primary-50": "...", ... "--primary-950": "..." }

3.5 运行时切换主题

<script setup>
import { usePreferences } from "@/core/preferences";

const { preferences, updatePreferences } = usePreferences();

// 切换内置主题
function changeBuiltinTheme(type) {
  updatePreferences({ theme: { builtinType: type } });
}

// 切换暗黑模式
function toggleDarkMode() {
  const currentMode = preferences.theme.mode;
  const isDark = currentMode === "dark" ||
    (currentMode === "auto" && window.matchMedia("(prefers-color-scheme: dark)").matches);
  updatePreferences({ theme: { mode: isDark ? "light" : "dark" } });
}

// 自定义主色
function changePrimaryColor(color) {
  updatePreferences({
    theme: { builtinType: "custom", colorPrimary: color },
  });
}
</script>

<template>
  <div>
    <el-button @click="changeBuiltinTheme('default')">默认蓝</el-button>
    <el-button @click="changeBuiltinTheme('violet')">紫色</el-button>
    <el-button @click="changePrimaryColor('#eb2f96')">自定义粉</el-button>
    <el-button @click="toggleDarkMode">切换暗黑模式</el-button>
  </div>
</template>

3.6 暗黑模式切换原理

// src/utils/theme.ts
export function toggleDarkMode(isDark: boolean) {
  if (isDark) {
    document.documentElement.classList.add("dark");
  } else {
    document.documentElement.classList.remove("dark");
  }
  // vxe-table 暗色主题同步
  VxeUI.setTheme(isDark ? "dark" : "light");
}

3.7 半暗模式

Vue Element 版支持半暗侧边栏和半暗顶栏(仅在浅色模式下生效):

updatePreferences({
  theme: {
    mode: "light",
    semiDarkSidebar: true,   // 侧边栏深色背景
    semiDarkHeader: false,    // 顶栏保持浅色
  },
});

四、React 版 — 主题定制

4.1 主题架构

React 版基于 Ant Design V6 的主题系统,核心位于 src/core/preferences/:

src/core/preferences/
├── config/
│   ├── constants.ts          # 内置主题预设(与 Vue Element 版一致)
│   └── default.ts            # 默认偏好配置
├── hooks/
│   ├── useThemeConfig.ts     # Ant Design ThemeConfig 生成
│   ├── usePreferences.ts     # 偏好 Hook
│   └── useLocale.ts          # 语言切换 Hook
├── store/
│   └── index.ts              # Zustand 偏好状态管理
└── types/
    └── theme.ts              # 主题类型定义

4.2 Ant Design ThemeConfig

React 版通过 useThemeConfig Hook 将偏好配置转换为 Ant Design 的 ThemeConfig:

// src/core/preferences/hooks/useThemeConfig.ts
import { theme as antdTheme, type ThemeConfig } from 'antd';
import { usePreferencesStore } from '../store';

export const useThemeConfig = (): ThemeConfig => {
    const { theme, app } = usePreferencesStore((state) => state.preferences);

    // 响应式跟踪系统暗色模式
    const [systemIsDark, useState] = useState(
        () => window.matchMedia('(prefers-color-scheme: dark)').matches,
    );

    // 计算有效主题
    const effectiveIsDark = useMemo(() => {
        if (theme.mode === 'dark') return true;
        if (theme.mode === 'light') return false;
        if (theme.mode === 'auto') return systemIsDark;
        return false;
    }, [theme.mode, systemIsDark]);

    return useMemo((): ThemeConfig => {
        const algorithms = [];
        if (effectiveIsDark) algorithms.push(antdTheme.darkAlgorithm);
        if (app.compact) algorithms.push(antdTheme.compactAlgorithm);

        return {
            algorithm: algorithms.length > 0 ? algorithms : antdTheme.defaultAlgorithm,
            token: {
                colorPrimary: theme.colorPrimary,
                colorSuccess: theme.colorSuccess,
                colorWarning: theme.colorWarning,
                colorError: theme.colorDestructive,
                borderRadius: Number.parseInt(theme.radius) || 6,
            },
        };
    }, [effectiveIsDark, theme, app]);
};

4.3 在应用中应用主题

// App.tsx 中使用
import { ConfigProvider, App as AntdApp } from 'antd';
import { useThemeConfig } from '@/core/preferences';

function App() {
    const themeConfig = useThemeConfig();

    return (
        <ConfigProvider theme={themeConfig}>
            <AntdApp>
                {/* 应用内容 */}
            </AntdApp>
        </ConfigProvider>
    );
}

4.4 运行时切换主题

import { usePreferencesStore } from '@/core/preferences/store';

function ThemeSwitcher() {
    const updatePreferences = usePreferencesStore((state) => state.updatePreferences);
    const preferences = usePreferencesStore((state) => state.preferences);

    const changePrimaryColor = (color: string) => {
        updatePreferences({
            theme: { colorPrimary: color },
        });
    };

    const toggleDarkMode = () => {
        const isDark = preferences.theme.mode === 'dark';
        updatePreferences({
            theme: { mode: isDark ? 'light' : 'dark' },
        });
    };

    return (
        <div>
            <Button onClick={() => changePrimaryColor('#1890ff')}>蓝色</Button>
            <Button onClick={() => changePrimaryColor('#722ed1')}>紫色</Button>
            <Button onClick={toggleDarkMode}>切换暗黑模式</Button>
        </div>
    );
}

4.5 默认偏好配置

React 版的默认配置与 Vue Element 版结构一致:

// src/core/preferences/config/default.ts
const defaultPreferences: Preferences = {
    // ...
    theme: {
        builtinType: "default",
        colorPrimary: "hsl(212 100% 45%)",     // 主色
        colorSuccess: "hsl(144 57% 58%)",       // 成功色
        colorWarning: "hsl(42 84% 61%)",        // 警告色
        colorDestructive: "hsl(348 100% 61%)",  // 危险色
        mode: "dark",
        radius: "0.5",
        semiDarkHeader: false,
        semiDarkSidebar: false,
    },
};

五、Vue Vben 版 — 国际化

5.1 语言包结构

Vue Vben 版的语言包位于 apps/admin/src/locales/,采用 JSON 格式按语言分目录:

locales/
├── langs/
│   ├── en-US/
│   │   ├── enum.json       # 枚举翻译
│   │   ├── menu.json       # 菜单翻译
│   │   ├── page.json       # 页面翻译
│   │   └── ui.json         # UI 翻译
│   └── zh-CN/
│       ├── enum.json
│       ├── menu.json
│       ├── page.json
│       └── ui.json
└── index.ts                 # 语言配置入口

5.2 语言配置入口

locales/index.ts 负责加载应用语言包和第三方组件库语言包:

import { $t, setupI18n as coreSetup, loadLocalesMapFromDir } from '@vben/locales';
import { preferences } from '@vben/preferences';
import antdDefaultLocale from 'ant-design-vue/es/locale/zh_CN';
import dayjs from 'dayjs';

const modules = import.meta.glob('./langs/**/*.json');
const localesMap = loadLocalesMapFromDir(
  /\.\/langs\/([^/]+)\/(.*)\.json$/,
  modules,
);

async function loadMessages(lang) {
  const [appLocaleMessages] = await Promise.all([
    localesMap[lang]?.(),
    loadThirdPartyMessage(lang),  // 加载 Ant Design Vue 和 dayjs 的语言包
  ]);
  return appLocaleMessages?.default;
}

async function setupI18n(app, options = {}) {
  await coreSetup(app, {
    defaultLocale: preferences.app.locale,
    loadMessages,
    missingWarn: !import.meta.env.PROD,
    ...options,
  });
}

5.3 在组件中使用

<script setup>
import { useI18n } from '@vben/locales';
const { t } = useI18n();
</script>

<template>
  <h1>{{ t('page.user.title') }}</h1>
  <Button>{{ t('common.save') }}</Button>
</template>

5.4 动态切换语言

import { usePreferences } from '@vben/preferences';

const { updatePreferences } = usePreferences();

// 切换到英文
updatePreferences({ app: { locale: 'en-US' } });

// 切换到中文
updatePreferences({ app: { locale: 'zh-CN' } });

六、Vue Element 版 — 国际化

6.1 语言包结构

Vue Element 版的语言包位于 src/locales/,同样采用 JSON 格式,但翻译文件更加细分:

locales/
├── en-US/
│   ├── app.json                      # 应用级翻译
│   ├── common.json                   # 通用翻译
│   ├── core.json                     # 核心功能翻译
│   ├── enum.json                     # 枚举翻译
│   ├── preferences.json              # 偏好设置翻译
│   ├── routes.json                   # 路由翻译
│   ├── validation.json               # 表单验证翻译
│   └── pages/                        # 页面级翻译
│       ├── user.json
│       ├── role.json
│       ├── menu.json
│       ├── tenant.json
│       └── ...(每个业务模块一个文件)
└── zh-CN/
    ├── app.json
    ├── common.json
    └── pages/
        └── ...

6.2 i18n 配置

Vue Element 版使用 vue-i18n 库,配置位于 src/core/i18n/setup.ts:

import { createI18n } from "vue-i18n";

const i18n = createI18n({
  globalInjection: true,
  legacy: false,
  locale: "",
  messages: {},
});

// 从 src/locales/ 加载翻译文件
const modules = import.meta.glob("../../locales/**/*.json");
const localesMap = loadLocalesMapFromDir(/..\/locales\/([^/]+)\/(.*)\.json$/, modules);

async function setupI18n(app, options = {}) {
  const { defaultLocale = "zh-CN" } = options;
  app.use(i18n);
  await loadLocaleMessages(defaultLocale);
}

// 运行时加载语言包
async function loadLocaleMessages(lang) {
  const message = await localesMap[lang]?.();
  if (message?.default) {
    i18n.global.setLocaleMessage(lang, message.default);
  }
  setI18nLanguage(lang);
}

6.3 在组件中使用

<script setup>
import { $t } from "@/core/i18n";
</script>

<template>
  <h1>{{ $t('pages.user.title') }}</h1>
  <el-button>{{ $t('common.save') }}</el-button>
</template>

6.4 动态切换语言

import { usePreferences } from "@/core/preferences";

const { updatePreferences } = usePreferences();

// 切换语言(自动触发 loadLocaleMessages)
updatePreferences({ app: { locale: "en-US" } });

语言切换时的自动处理流程(在 PreferenceManager.handleUpdates() 中):

// 当 locale 字段变化时,自动加载新语言包
if (Reflect.has(appUpdates, "locale") && appUpdates.locale) {
  loadLocaleMessages(appUpdates.locale);
}

6.5 翻译路由标题

import { translateRouteTitle } from "@/core/i18n";

// 用于面包屑、侧边栏、标签页等场景
const title = translateRouteTitle("routes.dashboard.title");
// 如果翻译存在则返回翻译文本,否则返回原文

七、React 版 — 国际化

7.1 语言包结构

React 版使用 i18next + react-i18next,语言包位于 src/locales/,采用命名空间分模块:

locales/
├── index.ts                    # 语言资源导出
├── zh-CN/
│   ├── index.ts                # 中文资源汇总
│   ├── _core/                  # 核心翻译
│   │   ├── common.json
│   │   ├── auth.json
│   │   ├── editor.json
│   │   └── routes.json
│   └── _modules/               # 业务模块翻译
│       ├── user.json
│       ├── role.json
│       ├── menu.json
│       └── ...
└── en-US/
    ├── index.ts
    ├── _core/
    └── _modules/

7.2 i18n 配置

React 版的 i18n 初始化位于 src/core/i18n/config/i18n.ts:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import { resources, allNamespaces, type SupportedLocale } from '@/locales';

export const initI18n = async (initialLang: SupportedLocale) => {
  await i18n.use(initReactI18next).init({
    lng: initialLang,
    resources,           // 核心 + 扩展模块全部预加载
    fallbackLng: 'zh-CN',
    supportedLngs: ['zh-CN', 'en-US'],
    defaultNS: 'common',
    ns: allNamespaces,   // 声明所有命名空间
    missingKeyHandler: import.meta.env.DEV
      ? (lngs, ns, key) => {
          console.warn(`[i18n] Missing: "${key}" in "${ns}" for "${lngs[0]}"`);
        }
      : undefined,
  });

  return i18n;
};

语言资源导出(src/locales/index.ts):

import { zhCN, type ZhCNResources, allNamespaces as zhCNNamespaces } from './zh-CN';
import { enUS, type EnUSResources } from './en-US';

const allNamespaces = zhCNNamespaces;

export const resources = {
    'zh-CN': zhCN,
    'en-US': enUS,
} as const;

export const supportedLocales = ['zh-CN', 'en-US'] as const;
export type SupportedLocale = typeof supportedLocales[number];

7.3 在组件中使用

import { useI18n } from '@/core/i18n/hooks';

function UserPage() {
    const { t } = useI18n('user');  // 指定命名空间

    return (
        <div>
            <h1>{t('title')}</h1>
            <Button>{t('common:save')}</Button>
        </div>
    );
}

7.4 动态切换语言

import { useI18n } from '@/core/i18n/hooks';

function LanguageSwitcher() {
    const { i18n, changeLocale } = useI18n();

    const switchToCN = async () => {
        await changeLocale('zh-CN');
    };

    const switchToEN = async () => {
        await changeLocale('en-US');
    };

    return (
        <Select
            value={i18n.language}
            onChange={(val) => changeLocale(val)}
            options={[
                { label: '简体中文', value: 'zh-CN' },
                { label: 'English', value: 'en-US' },
            ]}
        />
    );
}

7.5 类型安全翻译

React 版提供了类型安全的翻译函数:

// src/locales/index.ts 中的类型定义
export type CoreTFunction = <N extends CoreNamespace>(
    namespace: N,
    key: CoreKey<N>,
    params?: Record<string, any>
) => string;

八、添加新语言实战

以添加日语(ja-JP)为例,分别介绍三套前端的做法。

8.1 Vue Vben 版

  1. 创建语言文件目录 apps/admin/src/locales/langs/ja-JP/,添加 JSON 翻译文件:
// ja-JP/page.json
{
  "user": {
    "title": "ユーザー管理",
    "create": "ユーザー作成"
  }
}
  1. 注册第三方组件库语言包(修改 locales/index.ts):
async function loadAntdLocale(lang) {
  switch (lang) {
    case 'ja-JP': {
      antdLocale.value = await import('ant-design-vue/es/locale/ja_JP');
      break;
    }
    // ... 其他语言
  }
}

8.2 Vue Element 版

  1. 创建语言文件目录 src/locales/ja-JP/,复制 zh-CN/ 下的所有 JSON 文件并翻译。
  2. 由于语言包通过 import.meta.glob 自动扫描,无需额外注册。

8.3 React 版

  1. 创建语言文件目录 src/locales/ja-JP/,复制 zh-CN/ 下的目录结构和 JSON 文件并翻译。

  2. 更新 src/locales/index.ts:

    import { jaJP } from './ja-JP';
    
    export const resources = {
        'zh-CN': zhCN,
        'en-US': enUS,
        'ja-JP': jaJP,       // 新增
    } as const;
    
    export const supportedLocales = ['zh-CN', 'en-US', 'ja-JP'] as const;
    
  3. 更新 i18n 配置中的 supportedLngs。


九、日期时间本地化

三套前端均使用 dayjs 处理日期时间,需要同步设置 dayjs 语言:

9.1 Vue Vben 版

已在 locales/index.ts 中自动处理:

async function loadDayjsLocale(lang) {
  switch (lang) {
    case 'en-US': locale = await import('dayjs/locale/en'); break;
    case 'zh-CN': locale = await import('dayjs/locale/zh-cn'); break;
  }
  if (locale) dayjs.locale(locale);
}

9.2 Vue Element 版 / React 版

在语言切换时手动加载:

import dayjs from 'dayjs';

async function setDayjsLocale(locale) {
  const localeMap = {
    'zh-CN': 'zh-cn',
    'en-US': 'en',
  };
  dayjs.locale(localeMap[locale] || 'en');
}

十、实战场景

10.1 企业品牌定制(三套通用思路)

需求:将系统定制为企业品牌风格(蓝色主题 + 企业 Logo)。

Vue Vben 版:

// preferences.ts
export const overridesPreferences = defineOverridesPreferences({
  theme: { colorPrimary: '#0056b3', borderRadius: 4 },
  logo: { image: '/enterprise-logo.png', title: '企业管理平台' },
});

Vue Element 版:

// src/core/preferences/config/default.ts
const defaultPreferences = {
  theme: {
    builtinType: "custom",
    colorPrimary: "#0056b3",
    mode: "light",
    radius: "0.25",
  },
  logo: { source: "/enterprise-logo.png" },
  app: { name: "企业管理平台" },
};

React 版:

// src/core/preferences/config/default.ts
const defaultPreferences = {
  theme: {
    builtinType: "custom",
    colorPrimary: "#0056b3",
    mode: "light",
    radius: "0.25",
  },
  logo: { source: "/enterprise-logo.png" },
  app: { name: "企业管理平台" },
};

10.2 多租户主题隔离(三套通用思路)

根据租户 ID 动态加载主题色:

async function loadTenantTheme(tenantId, updatePreferences) {
  const themeConfig = await fetch(`/api/tenant/${tenantId}/theme`);
  updatePreferences({
    theme: { colorPrimary: themeConfig.primaryColor },
  });
}

10.3 节日主题切换

function isSpringFestival() {
  const month = new Date().getMonth() + 1;
  const day = new Date().getDate();
  return (month === 1 && day >= 20) || (month === 2 && day <= 20);
}

if (isSpringFestival()) {
  updatePreferences({ theme: { colorPrimary: '#f5222d' } });
}

十一、性能优化

11.1 主题切换优化

使用 CSS 变量避免组件重新渲染:

:root { --color-primary: #1890ff; }
.ant-btn-primary { background: var(--color-primary); }

切换时只更新 CSS 变量值,无需重建组件树。

11.2 懒加载语言包

Vue Vben / Vue Element 版:使用 import.meta.glob 实现按需加载:

const modules = import.meta.glob('./langs/**/*.json');
// 只有切换到对应语言时才加载该语言包

React 版:预加载所有语言包(适合语言数量少的场景):

resources: {
  'zh-CN': zhCN,  // 预加载
  'en-US': enUS,  // 预加载
}

11.3 偏好持久化

三套前端均将偏好保存到 localStorage:

// 保存
localStorage.setItem('preferences', JSON.stringify(preferences));

// 恢复
const cached = localStorage.getItem('preferences');
if (cached) preferences = JSON.parse(cached);

十二、常见问题

Q1: 主题切换后样式不生效?

检查:

  1. CSS 变量是否正确定义(浏览器 DevTools → Computed → 搜索变量名)
  2. 是否有更高优先级的样式覆盖
  3. 清除浏览器缓存后重试

Q2: Vue Element 版自定义颜色不生效?

确保 builtinType 设置为 "custom",否则内置主题预设会覆盖自定义颜色:

updatePreferences({
  theme: { builtinType: "custom", colorPrimary: "#ff0000" },
});

Q3: React 版暗黑模式不生效?

确保 ConfigProvider 的 theme 属性使用了 useThemeConfig() 的返回值,且 algorithm 包含 darkAlgorithm。

Q4: 语言切换后部分文本未翻译?

检查翻译文件中是否有对应的 key。各版本开发环境均会在控制台输出缺失 key 警告。

Q5: 如何适配移动端主题?

使用响应式断点:

@media (max-width: 768px) {
  :root {
    --el-border-radius-base: 4px;
    --sidebar-width: 200px;
  }
}

十三、相关文档

  • 前端架构总览
  • 前端核心功能详解
  • 后端配置与部署
Edit this page
Last Updated:: 6/5/26, 11:12 AM
Contributors: Bobo
Prev
事件总线与解耦架构教程
Next
性能优化与监控教程