154 lines
4.0 KiB
TypeScript
154 lines
4.0 KiB
TypeScript
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<string, string>;
|
|
}
|
|
|
|
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<boolean>) => {
|
|
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<ETheme>) => {
|
|
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;
|