看板
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import type { ExtractPropTypes, PropType } from 'vue';
|
||||
import { baseProps } from '../common/props';
|
||||
import { config, configProvider } from '../../libs';
|
||||
import type { Theme } from '../../types/global';
|
||||
|
||||
export const ConfigProviderProps = {
|
||||
...baseProps,
|
||||
/**
|
||||
* 主题风格,可选值:
|
||||
* - 'light': 强制亮色模式(默认)
|
||||
* - 'dark': 强制暗黑模式
|
||||
* - 'auto': 自动跟随系统设置
|
||||
*/
|
||||
darkMode: {
|
||||
type: String as PropType<'light' | 'dark' | 'auto'>,
|
||||
default: () => config.defaultDarkMode
|
||||
},
|
||||
/**
|
||||
* 当前主题名称(用于多主题切换)
|
||||
*/
|
||||
currentTheme: {
|
||||
type: String,
|
||||
default: () => configProvider.getCurrentTheme()?.name ?? config.defaultTheme
|
||||
},
|
||||
/**
|
||||
* 自定义主题列表
|
||||
*/
|
||||
themes: {
|
||||
type: Array as PropType<Theme[]>,
|
||||
default: () => configProvider.getThemes()
|
||||
},
|
||||
/**
|
||||
* 国际化配置:传入 locale 列表(数组)并可指定默认语言 currentLocale
|
||||
*/
|
||||
locales: {
|
||||
type: Array as PropType<any[]>,
|
||||
default: () => []
|
||||
},
|
||||
currentLocale: {
|
||||
type: String,
|
||||
default: () => configProvider.getCurrentLocale()?.name ?? ''
|
||||
}
|
||||
};
|
||||
|
||||
export type ConfigProviderProps = ExtractPropTypes<typeof ConfigProviderProps>;
|
||||
@@ -0,0 +1,189 @@
|
||||
<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);
|
||||
}
|
||||
);
|
||||
|
||||
// 计算合并样式(作为局部 fallback),configProvider 已经会把变量注入到 document 上
|
||||
const mergedThemeStyle = computed(() => {
|
||||
return $u.toStyle(configProvider.cssVarsRef.value, props.customStyle);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
Reference in New Issue
Block a user