146 lines
4.3 KiB
TypeScript
146 lines
4.3 KiB
TypeScript
/**
|
||
* 主题与 wot ConfigProvider 变量。
|
||
* 后台模式强制按暗色注入,避免用户浅色偏好导致表单字色/底色错乱。
|
||
*/
|
||
import { computed, ref } from 'vue'
|
||
import { useAppMode } from './useAppMode'
|
||
|
||
export type ThemePref = 'system' | 'light' | 'dark'
|
||
|
||
const THEME_KEY = 'theme-pref'
|
||
const themePref = ref<ThemePref>((uni.getStorageSync(THEME_KEY) as ThemePref) || 'system')
|
||
const isDark = ref(false)
|
||
|
||
function resolveDark(pref: ThemePref): boolean {
|
||
if (pref === 'dark') return true
|
||
if (pref === 'light') return false
|
||
try {
|
||
// 优先新 API,避免 wx.getSystemInfoSync 弃用警告
|
||
const base = typeof uni.getAppBaseInfo === 'function' ? uni.getAppBaseInfo() : null
|
||
if (base && 'theme' in base) {
|
||
return (base as { theme?: string }).theme === 'dark'
|
||
}
|
||
const info = uni.getSystemInfoSync()
|
||
return (info as UniApp.GetSystemInfoResult & { theme?: string }).theme === 'dark'
|
||
}
|
||
catch {
|
||
return false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 同步窗口底色到 art 主题色。
|
||
* theme.json 的 @bgColor 只跟系统深色;App 内选手动深色时必须靠此 API,避免 page 露白。
|
||
*/
|
||
function syncPageBackground(dark: boolean, admin = false) {
|
||
const bg = admin ? '#0a0a0a' : dark ? '#050505' : '#faf8f4'
|
||
try {
|
||
uni.setBackgroundColor({
|
||
backgroundColor: bg,
|
||
backgroundColorTop: bg,
|
||
backgroundColorBottom: bg,
|
||
})
|
||
}
|
||
catch { /* H5 may ignore */ }
|
||
try {
|
||
uni.setNavigationBarColor({
|
||
frontColor: dark || admin ? '#ffffff' : '#000000',
|
||
backgroundColor: bg,
|
||
})
|
||
}
|
||
catch { /* custom nav may ignore */ }
|
||
uni.setStorageSync('theme-bg-sync', `${dark || admin ? 1 : 0}-${Date.now()}`)
|
||
}
|
||
|
||
export function useTheme() {
|
||
const { appMode } = useAppMode()
|
||
|
||
/** 用户偏好暗色,或当前处于后台模式 */
|
||
const effectiveDark = computed(() => isDark.value || appMode.value === 'admin')
|
||
|
||
const apply = () => {
|
||
isDark.value = resolveDark(themePref.value)
|
||
syncPageBackground(isDark.value, appMode.value === 'admin')
|
||
}
|
||
|
||
const setThemePref = (pref: ThemePref) => {
|
||
themePref.value = pref
|
||
uni.setStorageSync(THEME_KEY, pref)
|
||
apply()
|
||
}
|
||
|
||
const themeLabel = computed(() => {
|
||
if (themePref.value === 'system') return '跟随系统'
|
||
if (themePref.value === 'light') return '浅色'
|
||
return '深色'
|
||
})
|
||
|
||
const wotTheme = computed(() => (effectiveDark.value ? 'dark' : 'light'))
|
||
|
||
/** 注入 wot ConfigProvider:主题色 + 导航/弹层/表单/搜索深色,避免白底割裂 */
|
||
const themeVars = computed(() => {
|
||
const dark = effectiveDark.value
|
||
const admin = appMode.value === 'admin'
|
||
const bg = dark ? '#121214' : '#ffffff'
|
||
const pageBg = admin ? '#0a0a0a' : dark ? '#050505' : '#faf8f4'
|
||
const title = dark ? '#ececec' : '#171717'
|
||
const muted = dark ? '#888888' : '#6b6b6b'
|
||
const border = dark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.08)'
|
||
return {
|
||
colorTheme: '#d4b383',
|
||
navbarColor: title,
|
||
navbarBg: admin
|
||
? 'rgba(10,10,10,0.92)'
|
||
: dark
|
||
? 'rgba(5,5,5,0.92)'
|
||
: 'rgba(250,248,244,0.92)',
|
||
navbarDescColor: title,
|
||
colorBg: bg,
|
||
colorTitle: title,
|
||
colorContent: title,
|
||
colorSecondary: muted,
|
||
darkColor: bg,
|
||
darkBackground: pageBg,
|
||
tabsNavBg: 'transparent',
|
||
tabsNavColor: muted,
|
||
tabsNavActiveColor: title,
|
||
tabsNavLineColor: '#d4b383',
|
||
tabsNavBorder: border,
|
||
cellBg: bg,
|
||
cellTitleColor: title,
|
||
cellValueColor: muted,
|
||
cellLabelColor: muted,
|
||
cellBorderColor: border,
|
||
collapseBg: bg,
|
||
collapseBodyBg: bg,
|
||
collapseHeaderBg: bg,
|
||
collapseTitleColor: title,
|
||
/* 后台表单需要可见底色,不能跟卡片同色透明 */
|
||
inputBg: dark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.03)',
|
||
inputInnerColor: title,
|
||
inputInnerPlaceholderColor: muted,
|
||
inputBorderColor: border,
|
||
textareaBg: dark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.03)',
|
||
textareaInnerColor: title,
|
||
textareaInnerPlaceholderColor: muted,
|
||
searchInputBg: dark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.04)',
|
||
searchInputColor: title,
|
||
searchPlaceholderColor: muted,
|
||
searchIconColor: muted,
|
||
}
|
||
})
|
||
|
||
apply()
|
||
|
||
return {
|
||
themePref,
|
||
isDark,
|
||
effectiveDark,
|
||
themeLabel,
|
||
wotTheme,
|
||
themeVars,
|
||
setThemePref,
|
||
apply,
|
||
}
|
||
}
|