Files
MiragenFlow/admin/src/modules/global/index.ts
T

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;