都快2026了,还有人不会国际化和暗黑主题适配吗,一篇文章彻底解决
国际化和暗黑主题适配
前端实现国际化和暗黑主题适配已成为现代产品的核心刚需:国际化能够突破语言、地区和文化壁垒,让产品适配全球不同用户的语言习惯、格式规范(日期/货币/地址)及合规要求,覆盖多语言网站、跨境电商、企业级SaaS等场景,是拓展用户群体、提升全球市场竞争力的基础;而暗黑主题适配不仅能在低光环境下保护用户视力、为OLED设备降低功耗,还能适配不同用户的视觉偏好与特殊人群的无障碍需求,同时契合科技、游戏等行业的产品调性,提升用户体验与使用舒适度;二者结合既满足了产品全球化的适配能力,又兼顾了用户个性化的体验诉求,是提升产品包容性、用户留存率和市场适配性的关键设计与开发策略。
1、国际化
国际化(i18n)是指让软件、网站或应用能够适配不同语言、地区和文化习惯的设计与开发过程,其核心目标是无需大规模修改代码,就能支持多语言、多地区的本地化部署 使用插件 i18n
1.1 定义存储当前语言的方法
src\utils\cookies.ts
import Cookies from 'js-cookie'
const languageKey = 'dd_language'
export const getLanguage = () => Cookies.get(languageKey)
export const setLanguage = (language:string) => Cookies.set(languageKey, language)
1.2 准备翻译文件
有的组件国际化语言较少,可能自行添加,比如vxe-table版本不是最新的,没有越南和俄罗斯翻译,需手动添加。
1.3 翻译文件
1.3.1 主文件内容
- src\lang\index.ts
- 注释已经很详细了,就是对翻译文件进行合并,然后根据当前选中的语言进行应用
- 重点介绍一下远程翻译,很多时候翻译的不是很准确,客户可能需要能自行进行翻译,我们可以在这里定义一个
loadRemoteMessages方法,用于加载远程翻译,后端返回给你远程自定义的翻译,然后通过扩展运算符进行合并,后面的数据会覆盖前面,从而实现替换,非常简单吧,根本没啥难度。 -
document.title能直接修改网页标题,可以通过这个设置网页标题多语言。
import { createI18n } from 'vue-i18n';
import { getLanguage } from '../utils/cookies';
// import { fetchRemoteTranslations } from '../api/translation' // 引入远程接口
// vxe-table 组件翻译(保留本地,不被远程覆盖)
import vxe_zhCN from 'vxe-table/lib/locale/lang/zh-CN';
import vxe_enUS from 'vxe-table/lib/locale/lang/en-US';
import vxe_ruRU from '@/lang/vxeTabel/ru_RU';
// ant-design 组件翻译(保留本地,不被远程覆盖)
import ant_zhCN from 'ant-design-vue/lib/locale/zh_CN';
import ant_enUS from 'ant-design-vue/lib/locale/en_US';
import ant_ruRU from 'ant-design-vue/lib/locale/ru_RU';
// dayjs 本地化(无需修改)
import "dayjs/locale/zh-cn";
import "dayjs/locale/en";
import "dayjs/locale/ru";
// 本地业务翻译(可能被远程覆盖)
import enLocale from './en';
import zhLocale from './zh';
import ruLocale from './ru';
// 初始化本地消息(组件库翻译 + 本地业务翻译)
const baseMessages = {
en_US: {
...ant_enUS, // 组件库翻译(优先级最高,不被覆盖)
...vxe_enUS,
...enLocale // 本地业务翻译(可能被远程覆盖)
},
zh_CN: {
...ant_zhCN,
...vxe_zhCN,
...zhLocale
},
ru_RU: {
...ant_ruRU,
...vxe_ruRU,
...ruLocale
}
}
// 获取当前语言(复用你的现有逻辑)
export const getLocale = () => {
const cookieLanguage = getLanguage()
if (cookieLanguage) {
document.documentElement.lang = cookieLanguage
return cookieLanguage
}
const language = navigator.language.toLowerCase()
const locales = Object.keys(baseMessages)
for (const locale of locales) {
if (language.indexOf(locale) > -1) {
document.documentElement.lang = locale
return locale
}
}
return 'zh_CN' // 默认中文
}
// 创建基础 i18n 实例(先加载本地翻译)
const i18n = createI18n({
legacy: false,
globalInjection: true,
locale: getLocale(),
messages: baseMessages, // 先使用本地基础翻译
silentTranslationWarn: true
})
// 核心:加载远程翻译并合并(远程覆盖本地业务翻译,不影响组件库翻译)
// 定义语言类型
type Locale = 'en_US' | 'zh_CN' | 'ru_RU';
// 修改 loadRemoteMessages 函数,添加类型约束
export const loadRemoteMessages = async (locale: Locale = getLocale()) => {
try {
// const remoteTrans = await fetchRemoteTranslations(locale)
let remoteTrans = {
99001: "测试远程翻译",
}
document.title = '波奇壁纸管理系统'
if (locale == 'en_US') {
remoteTrans = {
99001: "test this a remote translation",
}
document.title = 'bocchi wallpaper management system'
}
// 2. 合并规则:远程翻译覆盖本地业务翻译,但不影响组件库翻译
const merged = {
...baseMessages[locale], // 基础:组件库 + 本地业务
...remoteTrans // 远程业务翻译覆盖本地业务
}
// 3. 更新 i18n 实例的翻译数据
i18n.global.setLocaleMessage(locale, merged)
console.log(`已加载 ${locale} 远程翻译`)
} catch (err) {
console.error(`远程翻译加载失败,使用本地翻译:`, err)
// 失败时仍使用本地翻译
}
}
// 初始化时立即加载远程翻译(确保页面首次渲染使用最新翻译)
loadRemoteMessages()
export default i18n
1.3.1 翻译文件示例
src\lang\en.ts
export default {
// 系统 10
10001: '',
// 菜单 20
20001: 'Home',
20002: 'Wallpaper Management',
20003: 'Wallpaper Information',
20004: 'Image Upload',
20005: 'Video Upload',
20006: 'Data Management',
20007: 'User Management',
20008: 'User Information',
20009: 'Long-term Inactive',
20010: 'Entertainment Management',
20011: 'System Management',
20012: 'Port Information',
20013: 'Software Information'
}
引用并挂载多语言 src\main.ts
import { createApp } from 'vue'
import App from '@/App.vue'
import 'ant-design-vue/dist/reset.css'
import '@/styles/global.css'
import '@/styles/vxeTable.css'
import router from './router'
import VXETable from 'vxe-table'
import store from './store'
import 'vxe-table/lib/style.css'
import '@/router/permisstion'
import globalComponents from '@/components/index'
import globalDirective from '@/directives/index'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import './auto-update' // 自动更新
import i18n from './lang'
VXETable.setConfig({
size: 'mini', // 全局尺寸
i18n: (key: string, args: any) => i18n.global.t(key, args),
translate(key, args) {
return i18n.global.t(key, args)
}
})
const app = createApp(App)
// @ts-ignore
app.config.globalProperties.$i18n = i18n;
app.use(i18n)
app.use(router)
app.use(VXETable)
store.use(piniaPluginPersistedstate)
app.use(store)
app.use(globalComponents)
app.use(globalDirective)
app.mount('#app')
1.4 定义多语言切换组件
- src\components\layout\Language\index.vue
- 在合适的地方引入即可实现多语言切换
<template>
<div class="Language">
<a-dropdown>
<template #overlay>
<a-menu @click="onLanguage">
<a-menu-item v-for="itm in languageList" :key="itm.value" :name="itm.label">
{{ itm.label }}
</a-menu-item>
</a-menu>
</template>
<a-button style="width: 110px">
{{ language }}
<DownOutlined />
</a-button>
</a-dropdown>
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted } from 'vue';
import { DownOutlined } from '@ant-design/icons-vue';
import { useI18n } from "vue-i18n";
import { useUserStore } from '@/store/user'
import { storeToRefs } from 'pinia'
import { setLanguage } from '@/utils/cookies'
import { loadRemoteMessages } from '@/lang/index'
const { locale } = useI18n();
const store = useUserStore()
const { lang } = storeToRefs(store);
const emit = defineEmits(['on-click'])
//多语音
const language = computed(() => {
switch (lang.value) {
case 'zh_CN':
return '简体中文';
case 'en_US':
return 'English';
case 'ru_RU':
return 'Russia';
default:
return '简体中文';
}
})
const languageList = [
{ label: '简体中文', value: 'zh_CN' },
{ label: 'English', value: 'en_US' },
{ label: 'Russia', value: 'ru_RU' },
]
//切换语言
const onLanguage = async (v: any) => {
// language.value = v.item.name;
await loadRemoteMessages(v.key);
locale.value = v.key;
lang.value = v.key;
setLanguage(v.key)
emit('on-click')
}
onMounted(() => {
setLanguage(lang.value)
})
</script>
<style scoped lang="scss"></style>
1.5 主UI库适配
- src\App.vue
- 这里以
ant-design-vue为例,按照官网方法设置即可,其它 UI库同理。
<template>
<ConfigProvider :locale="language" :theme="themeConfig">
<router-view />
</ConfigProvider>
</template>
<script setup lang="ts">
import { computed, ref, watch, onMounted } from 'vue'
import 'dayjs/locale/zh-cn'
import { useUserStore } from '@/store/user.ts'
import themeStore from '@/store/themeStore'
import { storeToRefs } from 'pinia'
import zhCN from 'ant-design-vue/lib/locale/zh_CN'
import enUS from 'ant-design-vue/lib/locale/en_US'
import ruRU from 'ant-design-vue/lib/locale/ru_RU'
import { ConfigProvider, theme } from "ant-design-vue";
const userStore = useUserStore()
const { lang } = storeToRefs(userStore)
const { isDark } = storeToRefs(themeStore())
const themeConfig = ref({
algorithm: isDark.value ? theme.darkAlgorithm : theme.defaultAlgorithm, // 核心:切换明暗算法
});
watch(isDark, (newVal) => {
themeConfig.value.algorithm = newVal ? theme.darkAlgorithm : theme.defaultAlgorithm
document.documentElement.classList.toggle('dark', newVal);
})
const obj: any = {
'zh_CN': zhCN,
'en_US': enUS,
'ru_RU': ruRU
}
const language = computed(() => {
return obj[lang.value]
})
onMounted(() => {
document.documentElement.classList.toggle('dark', isDark.value);
})
</script>
<style lang="scss"></style>
1.6 使用
直接使用 $t('key') 或者 引入后使用 t('key') 都行
<template>
<div>
<hr>
{{ t('99001') }}
<hr>
{{ $t('99001') }}
<hr>
</div>
</template>
<script lang="ts" setup>
import { useI18n } from "vue-i18n";
const { t } = useI18n();
</script>
1.7 效果预览
2、暗黑模式
暗黑模式(Dark Mode)核心价值是适配特定行业调性。刚做的工厂mes系统,现场属于高亮环境,不同时间段还会有太阳光反射等情况,亮色模式下看不清,领导就要求加入暗黑模式,咱就给他加。
2.1 定义主题仓库
- src\store\themeStore.ts
- 用于记录主题和改变主题
- 仓库已做持久化存储
import { defineStore } from 'pinia'
const themeStore = defineStore('theme', {
state: (): {
isDark: boolean,
} => {
return {
isDark: false,
}
},
persist: true,
actions: {
toggleTheme() {
this.isDark = !this.isDark
},
},
getters: {},
})
export default themeStore
2.2 定义主题参数
- src\styles\global.css
2.2.1自定义 css 全局变量
- 分别定义 暗黑主题 和 亮色主题 的css变量
/* 基础变量:覆盖 90% 通用场景,与 Antd 风格对齐 */
:root {
--bg: white;
/* ========== 背景色 ========== */
--bg-primary: #ffffff;
--bg-secondary: #f5f5f5;
--bg-tertiary: #fafafa;
--bg-overlay: #ffffff;
/* ========== 文字色 ========== */
--text-primary: black;
--text-secondary: #4b5563;
--text-tertiary: #9ca3af;
--text-inverse: #ffffff;
/* ========== 边框色 ========== */
--border-primary: #e5e7eb;
--border-secondary: #d1d5db;
--border-hover: #9ca3af;
/* ========== 功能色(与 Antd 主色对齐) ========== */
--color-primary: #1890ff;
--color-success: #52c41a;
--color-warning: #faad14;
--color-danger: #ff4d4f;
--color-info: #1890ff;
/* ========== 交互状态 ========== */
--hover-bg: #f9fafb;
--active-bg: #f3f4f6;
--disabled-bg: #f9fafb;
/* 偏白色的主色调 */
--color-primary-light: #e6f7ff;
--color-primary-lighter: #bae7ff;
--color-primary-lightest: #69c0ff;
--color-primary-darker: #40a9ff;
--color-primary-darkest: #096dd9;
/* 白色透明梯度色 */
--color-gradient-light: rgba(255, 255, 255, 0.3);
--color-gradient-lighter: rgba(255, 255, 255, 0.4);
--color-gradient-lightest: rgba(255, 255, 255, 0.5);
/* 表格相关 */
--table-hover-bg: #f1f2ffe8;
--table-active-bg: #cee9ffe2;
/* vxe-table 专属变量 */
--vxe-table-bg: var(--bg-primary);
--vxe-table-header-bg: var(--bg-secondary);
--vxe-table-row-hover-bg: #f9fafb;
--vxe-table-row-active-bg: #f3f4f6;
--vxe-table-border-color: var(--border-primary);
--vxe-table-text-color: var(--text-primary);
--vxe-table-header-text-color: var(--text-primary);
--vxe-table-cell-hover-color: var(--color-primary);
}
/* 暗黑模式变量:基于 Antd 暗黑风格优化 */
.dark {
--bg: black;
/* ========== 背景色 ========== */
--bg-primary: #141414;
--bg-secondary: #1f1f1f;
--bg-tertiary: #272727;
--bg-overlay: #1f1f1f;
/* ========== 文字色 ========== */
--text-primary: white;
--text-secondary: #9ca3af;
--text-tertiary: #6b7280;
--text-inverse: #141414;
/* ========== 边框色 ========== */
--border-primary: #303030;
--border-secondary: #3d3d3d;
--border-hover: #474747;
/* ========== 功能色(暗黑模式优化) ========== */
--color-primary: #359eff;
--color-success: #67c23a;
--color-warning: #feb019;
--color-danger: #ff7875;
--color-info: #359eff;
/* ========== 交互状态 ========== */
--hover-bg: #272727;
--active-bg: #303030;
--disabled-bg: #1f1f1f;
/* 暗黑模式 - 偏暗的主色调(匹配原亮色蓝色系,适配深色背景) */
--color-primary-light: #142f40;
--color-primary-lighter: #103c58;
--color-primary-lightest: #2386c8;
--color-primary-darker: #2994e0;
--color-primary-darkest: #359eff;
/* 黑色透明梯度色 */
--color-gradient-light: rgba(0, 0, 0, 0.5);
--color-gradient-lighter: rgba(0, 0, 0, 0.6);
--color-gradient-lightest: rgba(0, 0, 0, 0.7);
/* 表格相关 */
--table-hover-bg: #4e4e4e;
--table-active-bg: #000a3aa4;
/* vxe-table 专属变量(适配暗黑) */
--vxe-table-bg: var(--bg-primary);
--vxe-table-header-bg: var(--bg-secondary);
--vxe-table-row-hover-bg: #272727;
--vxe-table-row-active-bg: #303030;
--vxe-table-border-color: var(--border-primary);
--vxe-table-text-color: var(--text-primary);
--vxe-table-header-text-color: var(--text-primary);
--vxe-table-cell-hover-color: var(--color-primary);
}
2.2.2 覆盖插件样式
- 对于一些没有适配暗黑模式或者暗黑模式不清晰的插件,可以对样式进行覆盖
- 这里以 vxe-table 为例,官网的设置暗黑方法不存在,可能是版本原因吧
- 其它插件同理覆盖即可
/* 覆盖 vxe-table 基础样式 - 全局生效 */
.vxe-table {
--vxe-font-color: var(--vxe-table-text-color) !important;
--vxe-table-header-font-color: var(--vxe-table-header-text-color) !important;
--vxe-table-row-hover-bg-color: var(--vxe-table-row-hover-bg) !important;
--vxe-table-row-current-bg-color: var(--vxe-table-row-active-bg) !important;
--vxe-table-row-hover-font-color: var(--vxe-table-text-color) !important;
--vxe-border-color: var(--vxe-table-border-color) !important;
--vxe-table-header-bg-color: var(--vxe-table-header-bg) !important;
--vxe-table-body-bg-color: var(--vxe-table-bg) !important;
--vxe-table-empty-bg-color: var(--vxe-table-bg) !important;
--vxe-input-border-color: var(--vxe-table-border-color) !important;
--vxe-input-bg-color: var(--vxe-table-bg) !important;
--vxe-input-font-color: var(--vxe-table-text-color) !important;
--vxe-border-color: var(--border-primary) !important;
--vxe-table-border-color: var(--border-primary) !important;
}
.vxe-header--row {
background-color: var(--bg-primary) !important;
}
.row--level-0 {
background-color: var(--bg-secondary) !important;
}
.row--stripe {
background-color: var(--bg-tertiary) !important;
}
.vxe-pager {
background-color: var(--bg-primary) !important;
}
.vxe-loading {
background-color: var(--color-gradient-light) !important;
}
.vxe-input--inner,
.vxe-pager--jump-prev,
.vxe-select--panel-wrapper,
.vxe-pager--prev-btn,
.vxe-pager--num-btn,
.vxe-pager--next-btn,
.vxe-pager--jump-next,
.vxe-pager--goto,
.vxe-pager {
background-color: var(--bg-secondary) !important;
border-color: var(--border-primary) !important;
color: var(--text-secondary) !important;
}
/* 进入行 */
.row--hover {
background-color: var(--table-hover-bg) !important;
}
/* 选中行 */
.row--current {
background-color: var(--table-active-bg) !important;
}
.vxe-table--body-wrapper {
background-color: var(--bg-secondary) !important;
}
.vxe-toolbar {
background-color: var(--bg-primary) !important;
border-color: var(--border-primary) !important;
color: var(--text-secondary) !important;
}
/*滚动条整体部分*/
.vxe-table ::-webkit-scrollbar {
width: 8px !important;
height: 8px !important;
}
/*滚动条的轨道*/
.vxe-table ::-webkit-scrollbar-track {
background: transparent !important;
}
/*滚动条里面的小方块,能向上向下移动*/
.vxe-table ::-webkit-scrollbar-thumb {
background-color: var(--bg-secondary) !important;
border-radius: 5px !important;
border: 1px solid var(--border-primary) !important;
box-shadow: inset 0 0 6px var(--text-secondary) !important;
}
/*边角,即两个滚动条的交汇处*/
.vxe-table ::-webkit-scrollbar-corner {
background-color: var(--bg-secondary) !important;
}
.vxe-pager--btn-wrapper .is--active {
background-color: var(--color-primary) !important;
color: white !important;
}
/* 覆盖 vxe-table 基础样式 - 全局生效 */
.vxe-table {
--vxe-font-color: var(--vxe-table-text-color) !important;
--vxe-table-header-font-color: var(--vxe-table-header-text-color) !important;
--vxe-table-row-hover-bg-color: var(--vxe-table-row-hover-bg) !important;
--vxe-table-row-current-bg-color: var(--vxe-table-row-active-bg) !important;
--vxe-table-row-hover-font-color: var(--vxe-table-text-color) !important;
--vxe-border-color: var(--vxe-table-border-color) !important;
--vxe-table-header-bg-color: var(--vxe-table-header-bg) !important;
--vxe-table-body-bg-color: var(--vxe-table-bg) !important;
--vxe-table-empty-bg-color: var(--vxe-table-bg) !important;
--vxe-input-border-color: var(--vxe-table-border-color) !important;
--vxe-input-bg-color: var(--vxe-table-bg) !important;
--vxe-input-font-color: var(--vxe-table-text-color) !important;
--vxe-border-color: var(--border-primary) !important;
--vxe-table-border-color: var(--border-primary) !important;
}
.vxe-header--row {
background-color: var(--bg-primary) !important;
}
.row--level-0 {
background-color: var(--bg-secondary) !important;
}
.row--stripe {
background-color: var(--bg-tertiary) !important;
}
.vxe-pager {
background-color: var(--bg-primary) !important;
}
.vxe-loading {
background-color: var(--color-gradient-light) !important;
}
.vxe-input--inner,
.vxe-pager--jump-prev,
.vxe-select--panel-wrapper,
.vxe-pager--prev-btn,
.vxe-pager--num-btn,
.vxe-pager--next-btn,
.vxe-pager--jump-next,
.vxe-pager--goto,
.vxe-pager {
background-color: var(--bg-secondary) !important;
border-color: var(--border-primary) !important;
color: var(--text-secondary) !important;
}
/* 进入行 */
.row--hover {
background-color: var(--table-hover-bg) !important;
}
/* 选中行 */
.row--current {
background-color: var(--table-active-bg) !important;
}
.vxe-table--body-wrapper {
background-color: var(--bg-secondary) !important;
}
.vxe-toolbar {
background-color: var(--bg-primary) !important;
border-color: var(--border-primary) !important;
color: var(--text-secondary) !important;
}
/*滚动条整体部分*/
.vxe-table ::-webkit-scrollbar {
width: 8px !important;
height: 8px !important;
}
/*滚动条的轨道*/
.vxe-table ::-webkit-scrollbar-track {
background: transparent !important;
}
/*滚动条里面的小方块,能向上向下移动*/
.vxe-table ::-webkit-scrollbar-thumb {
background-color: var(--bg-secondary) !important;
border-radius: 5px !important;
border: 1px solid var(--border-primary) !important;
box-shadow: inset 0 0 6px var(--text-secondary) !important;
}
/*边角,即两个滚动条的交汇处*/
.vxe-table ::-webkit-scrollbar-corner {
background-color: var(--bg-secondary) !important;
}
.vxe-pager--btn-wrapper .is--active {
background-color: var(--color-primary) !important;
color: white !important;
}
.vxe-icon-checkbox-unchecked {
color: var(--text-tertiary) !important;
}
2.3 引入定义好的变量和样式
- src\main.ts
import { createApp } from 'vue'
import App from '@/App.vue'
import 'ant-design-vue/dist/reset.css'
import '@/styles/global.css'
import '@/styles/vxeTable.css'
import router from './router'
import VXETable from 'vxe-table'
import store from './store'
import 'vxe-table/lib/style.css'
import '@/router/permisstion'
import globalComponents from '@/components/index'
import globalDirective from '@/directives/index'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import './auto-update' // 自动更新
import i18n from './lang'
VXETable.setConfig({
size: 'mini', // 全局尺寸
i18n: (key: string, args: any) => i18n.global.t(key, args),
translate(key, args) {
return i18n.global.t(key, args)
}
})
const app = createApp(App)
// @ts-ignore
app.config.globalProperties.$i18n = i18n;
app.use(i18n)
app.use(router)
app.use(VXETable)
store.use(piniaPluginPersistedstate)
app.use(store)
app.use(globalComponents)
app.use(globalDirective)
app.mount('#app')
2.4 定义主题切换组件
- src\components\layout\theme\index.vue
- 在合适的地方引入即可实现主题切换
<template>
<a-button class="themeButton" @click="toggleDark">
<img :src="isDark ? dark : light" alt="" style="width: 20px; height: 20px">
</a-button>
</template>
<script setup lang="ts">
import themeStore from '@/store/themeStore'
import { storeToRefs } from 'pinia'
import dark from '@/assets/images/dark.png'
import light from '@/assets/images/light.png'
const themes = themeStore()
const { isDark } = storeToRefs(themes)
const toggleDark = () => {
isDark.value = !isDark.value;
};
</script>
<style scoped lang="scss">
.themeButton{
cursor: pointer;
height: 32px;
width: 32px;
box-sizing: border-box;
border: 1px solid var(--border-primary);
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
}
</style>
2.5 主UI库适配、应用主题
- src\App.vue
- 这里以
ant-design-vue为例,按照官网方法设置即可,其它 UI库同理。 - 原理很简单,就是监听是否是暗黑主题,如果是则为根元素添加
dark类名,不是则移除。 记得初始化时默认设置一次类名
<template>
<ConfigProvider :locale="language" :theme="themeConfig">
<router-view />
</ConfigProvider>
</template>
<script setup lang="ts">
import { computed, ref, watch, onMounted } from 'vue'
import 'dayjs/locale/zh-cn'
import { useUserStore } from '@/store/user.ts'
import themeStore from '@/store/themeStore'
import { storeToRefs } from 'pinia'
import zhCN from 'ant-design-vue/lib/locale/zh_CN'
import enUS from 'ant-design-vue/lib/locale/en_US'
import ruRU from 'ant-design-vue/lib/locale/ru_RU'
import { ConfigProvider, theme } from "ant-design-vue";
const userStore = useUserStore()
const { lang } = storeToRefs(userStore)
const { isDark } = storeToRefs(themeStore())
const themeConfig = ref({
algorithm: isDark.value ? theme.darkAlgorithm : theme.defaultAlgorithm, // 核心:切换明暗算法
});
watch(isDark, (newVal) => {
themeConfig.value.algorithm = newVal ? theme.darkAlgorithm : theme.defaultAlgorithm
document.documentElement.classList.toggle('dark', newVal);
})
const obj: any = {
'zh_CN': zhCN,
'en_US': enUS,
'ru_RU': ruRU
}
const language = computed(() => {
return obj[lang.value]
})
onMounted(() => {
document.documentElement.classList.toggle('dark', isDark.value);
})
</script>
<style lang="scss"></style>
2.6 使用
- 使用就非常简单了,通过
var()使用自定义的css变量即可。
.form{
background-color: var(--bg-primary);
color: var(--text-primary);
}
2.7 效果预览
- 效果非常明显,暗黑模式在高亮环境下依然清晰可见。