import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { ETheme } from 'types/index.d'; import { Color } from 'tvision-color'; import { CHART_COLORS, defaultColor } from 'configs/color'; import { generateColorMap, insertThemeStylesheet } from 'utils/color'; import { RootState } from '../store'; import { version } from '../../../package.json'; const namespace = 'global'; export enum ELayout { side = 1, top, mix, fullPage, } export interface IGlobalState { loading: boolean; collapsed: boolean; /** * 是否显示面包屑导航 */ setting: boolean; version: string; color: string; /** * 主题:深色 浅色 */ theme: ETheme; /** * 是否开启跟随系统主题 */ systemTheme: boolean; layout: ELayout; isFullPage: boolean; showHeader: boolean; showBreadcrumbs: boolean; showFooter: boolean; chartColors: Record; } const defaultTheme = ETheme.light; const initialState: IGlobalState = { loading: true, collapsed: window.innerWidth < 1000, // 宽度小于1000 菜单闭合 setting: false, version, theme: defaultTheme, systemTheme: false, layout: ELayout.side, isFullPage: false, color: defaultColor?.[0], showHeader: true, showBreadcrumbs: true, showFooter: true, chartColors: CHART_COLORS[defaultTheme], }; // 创建带有命名空间的reducer const globalSlice = createSlice({ name: namespace, initialState, reducers: { toggleMenu: (state, action) => { if (action.payload === null) { state.collapsed = !state.collapsed; } else { state.collapsed = !!action.payload; } }, toggleSetting: (state) => { state.setting = !state.setting; }, setSetting: (state, action: PayloadAction) => { state.setting = Boolean(action.payload); }, toggleShowHeader: (state) => { state.showHeader = !state.showHeader; }, toggleShowBreadcrumbs: (state) => { state.showBreadcrumbs = !state.showBreadcrumbs; }, toggleShowFooter: (state) => { state.showFooter = !state.showFooter; }, switchTheme: (state, action: PayloadAction) => { const finalTheme = action?.payload; // 切换 chart 颜色 state.chartColors = CHART_COLORS[finalTheme]; // 切换主题颜色 state.theme = finalTheme; // 关闭跟随系统 state.systemTheme = false; document.documentElement.setAttribute('theme-mode', finalTheme); }, openSystemTheme: (state) => { const media = window.matchMedia('(prefers-color-scheme:dark)'); const finalTheme = media.matches ? ETheme.dark : ETheme.light; state.chartColors = CHART_COLORS[finalTheme]; // 切换主题颜色 state.theme = finalTheme; state.systemTheme = true; document.documentElement.setAttribute('theme-mode', finalTheme); }, switchColor: (state, action) => { if (action?.payload) { state.color = action?.payload; const mode = state.theme; const hex = action?.payload; const { colors: newPalette, primary: brandColorIndex } = Color.getColorGradations({ colors: [hex], step: 10, remainInput: false, // 是否保留输入 不保留会矫正不合适的主题色 })[0]; const newColorMap = generateColorMap(hex, newPalette, mode, brandColorIndex); insertThemeStylesheet(hex, newColorMap, mode); document.documentElement.setAttribute('theme-color', hex || ''); } }, switchLayout: (state, action) => { if (action?.payload) { state.layout = action?.payload; } }, switchFullPage: (state, action) => { state.isFullPage = !!action?.payload; }, }, extraReducers: () => {}, }); export const selectGlobal = (state: RootState) => state.global; export const { toggleMenu, toggleSetting, setSetting, toggleShowHeader, toggleShowBreadcrumbs, toggleShowFooter, switchTheme, switchColor, switchLayout, switchFullPage, openSystemTheme, } = globalSlice.actions; export default globalSlice.reducer;