CNCEC_APP/uni_modules/uview-pro/components/u-config-provider/u-config-provider.vue

190 lines
5.7 KiB
Vue
Raw Permalink Normal View History

2026-03-25 14:54:15 +08:00
<template>
<view :class="['u-config-provider', `u-theme-${darkMode}`, customClass]" :style="mergedThemeStyle">
<slot />
</view>
</template>
<script lang="ts">
/**
* u-config-provider
*
* 说明简要
* - 初始化场景推荐在应用入口执行使用 `useTheme().initTheme(themes)` 进行一次性初始化与全局设置例如在 `main.ts` `App.vue`
* - 组件/页面场景推荐使用 `useTheme()` 组合式函数在组件内部读取响应式 `currentTheme``themes``darkMode` 并通过 `setTheme()` `setDarkMode()` 切换主题/模式
*
* 该组件的行为
* - 如果在挂载时传入 `themes`会调用 `configProvider.initTheme(themes)`
* - 如果传入 `currentTheme`会优先设置当前主题
* - 如果传入 `darkMode`会设置当前的暗黑模式状态同时在 document 上添加 `u-theme-dark` `u-theme-light` 类名
*
* 详尽说明请参考`docs/config-provider-usage.md`
*/
export default {
name: 'u-config-provider',
options: {
addGlobalClass: true,
// #ifndef MP-TOUTIAO
virtualHost: true,
// #endif
styleIsolation: 'shared'
}
};
</script>
<script lang="ts" setup>
import { computed, watch, onMounted } from 'vue';
import { ConfigProviderProps } from './types';
import { $u, configProvider } from '../../libs';
import { useTheme } from '../../libs/hooks/useTheme';
import { useLocale } from '../../libs/hooks/useLocale';
const props = defineProps(ConfigProviderProps);
const emit = defineEmits<{
'theme-change': [themeName: string];
'mode-change': [mode: 'light' | 'dark'];
}>();
// 计算当前的主题模式(亮色/暗黑)
const darkMode = computed(() => (configProvider.isInDarkMode() ? 'dark' : 'light'));
const bootstrapTheme = () => {
// 如果已经初始化过主题,不再重复初始化,只更新 props 相关配置
const existingThemes = configProvider.getThemes();
if (existingThemes.length > 0) {
// 已初始化,只更新当前主题和暗黑模式
if (props.currentTheme) {
configProvider.setTheme(props.currentTheme as string);
}
if (props.darkMode) {
configProvider.setDarkMode(props.darkMode);
}
return;
}
// 未初始化,进行初始化
if (props.themes && props.themes.length) {
configProvider.initTheme(props.themes, props.currentTheme as any);
} else {
// 使用 useTheme 的 initTheme它会处理默认主题
const { initTheme } = useTheme();
initTheme(undefined, props.currentTheme as any);
}
if (props.currentTheme) {
configProvider.setTheme(props.currentTheme as string);
}
if (props.darkMode) {
configProvider.setDarkMode(props.darkMode);
}
};
const bootstrapLocale = () => {
// 初始化国际化
try {
const { initLocales, setLocale, getLocales } = useLocale();
const existingLocales = getLocales();
if (existingLocales.length > 0) {
if (props.currentLocale) {
setLocale(props.currentLocale as string);
}
} else {
if (props.locales && props.locales.length) {
initLocales(props.locales, props.currentLocale as any);
} else {
initLocales(undefined, props.currentLocale as any);
}
}
} catch (e) {
console.warn('[u-config-provider] init locales failed', e);
}
};
// 当传入自定义 themes 时,初始化全局 configProvider覆盖已有
onMounted(() => {
bootstrapTheme();
bootstrapLocale();
});
// 监听外部 props 变化(如果上层修改 prop
watch(
() => props.themes,
val => {
// 如果传入的 themes 来自于 configProvider 自身(常见于模板中使用 useTheme() 直接透传),
// 那么对其做深度监听会在我们内部更新主题对象时触发该回调,进而再次调用 init 导致循环更新。
// 为避免该情况,先做简单保护:当传入对象正是 configProvider.themesRef.value 时不重复初始化。
if (val && val.length && val !== configProvider.themesRef.value) {
configProvider.initTheme(val, props.currentTheme as any);
}
},
{ deep: true }
);
watch(
() => props.currentTheme,
val => {
if (val) {
configProvider.setTheme(val);
}
}
);
watch(
() => props.darkMode,
val => {
if (val && val !== configProvider.getDarkMode()) {
configProvider.setDarkMode(val);
emit('mode-change', darkMode.value);
}
}
);
// 监听 locales prop 变化
watch(
() => props.locales,
val => {
if (val && val.length) {
const { initLocales } = useLocale();
initLocales(val, props.currentLocale as any);
}
},
{ deep: true }
);
watch(
() => props.currentLocale,
val => {
if (val) {
const { setLocale } = useLocale();
setLocale(val);
}
}
);
// 监听全局主题变更并触发事件
watch(
() => configProvider.currentThemeRef.value,
(val, oldVal) => {
if (val && val.name !== (oldVal as any)?.name) {
emit('theme-change', val.name);
}
},
{ immediate: true }
);
// 监听暗黑模式变更并触发事件
watch(
() => configProvider.darkModeRef.value,
() => {
emit('mode-change', darkMode.value);
}
);
// 计算合并样式(作为局部 fallbackconfigProvider 已经会把变量注入到 document 上
const mergedThemeStyle = computed(() => {
return $u.toStyle(configProvider.cssVarsRef.value, props.customStyle);
});
</script>
<style lang="scss" scoped></style>