Skip to content

Commit

Permalink
feat: theme color
Browse files Browse the repository at this point in the history
  • Loading branch information
zzZ committed Nov 12, 2024
1 parent 3b5b2a4 commit 1d57456
Show file tree
Hide file tree
Showing 41 changed files with 526 additions and 247 deletions.
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,14 @@ react-antd-console 是一个后台管理系统的前端解决方案,封装了

## 功能

- **最新技术栈**: `Vite`(支持`热更新`)、`React18``Ant Design5``TypeScript`(近乎`100%`的类型覆盖)。
- **专注业务**: 封装好的布局(侧边菜单、面包屑、标签页、页头页脚等),只需要`专注于业务开发`
- **权限管理**: 支持`菜单级``按钮级`权限。
- **路由配置**: 一份极简配置,自动生成路由、菜单、面包屑等,支持嵌套路由、单/无布局等配置,支持路由动态变化等。
- **数据管理**: `分层`(数据和视图)架构设计,数据管理方案理论上支持接入任意UI渲染库/框架(包括不限于React/Vue/Angular)。
- **颜色换肤**: 支持深/浅肤色模式下的任意颜色切换。(主题版)
- **风格主题**: 不同的主题风格选择,如`布局``菜单``标签页``面包屑``页头页脚``动画`等。(主题版)
- **丰富组件**: 如`搜索表格``引导``富文本``Markdown`等。(主题版)
- **其他功能**: 如`响应式设计``国际化``Mock``环境配置``工程化规范`等。
- **最新技术栈**: `Vite`(支持`热更新`)、`React18``Ant Design5``TypeScript`(近乎`100%`的类型覆盖)
- **专注业务**: 封装好的布局(侧边菜单、面包屑、标签页、页头页脚等),只需要`专注于业务开发`
- **权限管理**: 支持`菜单级``按钮级`权限
- **路由配置**: 一份极简配置,自动生成路由、菜单、面包屑等,支持嵌套路由、单/无布局等配置,支持路由动态变化等
- **数据管理**: `分层`(数据和视图)架构设计,数据管理方案理论上支持接入任意UI渲染库/框架(包括不限于React/Vue/Angular)
- **颜色换肤**: 支持深/浅肤色模式下的任意颜色切换
- **丰富组件**: 如`搜索表格``引导``富文本``Markdown`
- **其他功能**: 如`响应式设计``国际化``Mock``环境配置``工程化规范`

## 快速开始

Expand Down
2 changes: 1 addition & 1 deletion docs/guide/what.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ react-antd-console 是一个后台管理系统的前端解决方案,封装了

## 谁适合使用?

如果你将要开发或学习如何开发后台管理系统。并且你是一名新手前端开发者,或者是一名了解一些前端的后端开发者,或者是一名有一定经验的前端开发者,但当下还没有信心把握所有的事情,并且希望循序渐进掌握,那么可以尝试本项目。本项目是作者多年经验的总结,可放心用于生产环境
如果你正在寻找一款极简的后台管理的前端模板,技术栈能先进且稳定,希望毫不费力地掌握其中的原理,或希望只专注于业务开发,那么可以尝试本项目。本项目是作者多年经验的总结,可放心用于生产环境

## 尽可能简单

Expand Down
1 change: 1 addition & 0 deletions src/assets/svg/color_picker.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/svg/color_picker2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/assets/svg/github.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/svg/theme_dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/svg/theme_light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 7 additions & 2 deletions src/components/AntdProvider/Provider.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { PropsWithChildren } from 'react';
import { ConfigProvider, App as AntdApp } from 'antd';
import { useAntdTheme } from './hooks';
import enUS from 'antd/locale/en_US';
import zhCN from 'antd/locale/zh_CN';
import { useModel } from '@zhangsai/model';
import { baseModel } from '@/models/base';
import { Locale } from 'antd/lib/locale';
import AntdStaticFnInit from '.';
import './index.less';

const languageMap: Record<string, Locale> = {
Expand All @@ -14,10 +16,13 @@ const languageMap: Record<string, Locale> = {

const AntdProvider = ({ children }: PropsWithChildren) => {
const language = useModel(baseModel, 'language');
const antdTheme = useAntdTheme();
return (
<ConfigProvider locale={languageMap[language]}>
<ConfigProvider theme={antdTheme} locale={languageMap[language]}>
<AntdApp className="console-antd-app">
{ children }
<AntdStaticFnInit>
{ children }
</AntdStaticFnInit>
</AntdApp>
</ConfigProvider>
);
Expand Down
19 changes: 19 additions & 0 deletions src/components/AntdProvider/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useModel } from '@zhangsai/model';
import { themeColors, themeModel } from '@/models/theme';
import { ThemeConfig, theme } from 'antd';

/** 根据themeModel映射一份antd theme的配置 */
export function useAntdTheme(darkMode?: boolean): ThemeConfig {
const curDarkMode = useModel(themeModel, 'curDarkMode');
const colorPrimary = useModel(themeModel, 'colorPrimary');
const isDark = (darkMode ?? curDarkMode);

return {
algorithm: isDark ? theme.darkAlgorithm : theme.defaultAlgorithm,
token: {
colorPrimary,
colorBgLayout: isDark ? themeColors.dark['--layout-background-color'] : themeColors.light['--layout-background-color'],
colorBgContainer: isDark ? themeColors.dark['--container-background-color'] : themeColors.light['--container-background-color'],
},
};
}
30 changes: 30 additions & 0 deletions src/components/AntdProvider/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { PropsWithChildren, useEffect } from 'react';
import { App as AntdApp } from 'antd';
import type { MessageInstance } from 'antd/lib/message/interface';
import type { ModalStaticFunctions } from 'antd/lib/modal/confirm';
import type { NotificationInstance } from 'antd/lib/notification/interface';

let message: MessageInstance;
let notification: NotificationInstance;
let modal: Omit<ModalStaticFunctions, 'warn'>;

const AntdStaticFnInit = ({ children }: PropsWithChildren) => {
const {
message: antdMessage,
notification: antdNotification,
modal: antdModal,
} = AntdApp.useApp();

useEffect(() => {
message = antdMessage;
notification = antdNotification;
modal = antdModal;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return children;
};

export default AntdStaticFnInit;

export { message, notification, modal };
5 changes: 3 additions & 2 deletions src/components/Progress/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { getRandomNumber } from '@/utils';
import NProgress from 'nprogress';
import { useLocation } from 'react-router-dom';
import { useEffect, useRef } from 'react';
import { theme } from 'antd';
import { setNProgressColor } from './utils';
import { useModel } from '@zhangsai/model';
import { themeModel } from '@/models/theme';
import 'nprogress/nprogress.css';

NProgress.configure({ showSpinner: false });
Expand All @@ -20,7 +21,7 @@ function act() {
const Progress = () => {
const { pathname } = useLocation();
const mountedRef = useRef(false);
const { colorPrimary } = theme.useToken().token;
const colorPrimary = useModel(themeModel, 'colorPrimary');

// 主题色变化时更新NProgress颜色
useEffect(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/http/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import axios from 'axios';
import { statusClass } from './utils';
import type { AxiosResponse, AxiosError, AxiosInstance } from '@/http/axios.d';
import { lsGetToken } from '@/utils/business/token';
import { message } from 'antd';
import { message } from '@/components/AntdProvider';
import { history } from '@/router';
import { t } from 'i18next';

Expand Down
52 changes: 52 additions & 0 deletions src/layouts/ColorPicker/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import TooltipIcon from '../components/TooltipIcon';
import SvgIcon from '@/components/SvgIcon';
import { themeModel } from '@/models/theme';
import { useModel } from '@zhangsai/model';
import { ColorPicker as AntdColorPicker } from 'antd';
import { useTranslation } from 'react-i18next';

export const ColorPrimaryPreset = [{
label: 'red',
colors: ['#d7003a', '#e2041b', '#c9171e', '#bf242a', '#b94047', '#a73836', '#d9333f', '#a22041', '#d0576b', '#e9546b', '#ec6d71', '#c85179', '#e7609e', '#a25768', '#bb5548'],
}, {
label: 'origin',
colors: ['#ea5506', '#eb6238', '#ec6d51', '#ed6d3d', '#ee7800', '#e17b34', '#f08300', '#f39800', '#f8b862', '#deb068', '#bc763c', '#ad7d4c', '#b68d4c', '#ae7c58', '#e6b422', '#f8b500'],
}, {
label: 'green',
colors: ['#69b076', '#88cb7f', '#98d98e', '#93b881', '#769164', '#316745', '#47885e', '#007b43', '#38b48b', '#3b7960', '#028760', '#7ebea5', '#00552e', '#005243'],
}, {
label: 'blue',
colors: ['#1c305c', '#223a70', '#164a84', '#19448e', '#165e83', '#507ea4', '#5a79ba', '#007bbb', '#0095d9', '#2ca9e1', '#2a83a2', '#008899', '#84b9cb', '#84a2d4', '#a0d8ef'],
}, {
label: 'purple',
colors: ['#4a488e', '#867ba9', '#a59aca', '#674598', '#9079ad', '#65318e', '#522f60', '#493759', '#884898', '#460e44', '#74325c', '#55295b', '#7a4171', '#9d5b8b', '#bc64a4', '#aa4c8f'],
}];

export const AntdColorPickerColorPrimaryPreset = ColorPrimaryPreset.map(item => {
return {
label: item.label,
colors: item.colors.slice(0),
};
});

const ColorPicker = () => {
const colorPrimary = useModel(themeModel, 'colorPrimary');
const { t: t_layout } = useTranslation('layout');

return (
<AntdColorPicker
value={colorPrimary}
onChange={(val) => {
themeModel.setThemeState({ colorPrimary: val.toHexString() });
}}
presets={AntdColorPickerColorPrimaryPreset}
>
<TooltipIcon
title={t_layout('主题色')}
icon={<SvgIcon name="color_picker" size={20} color={colorPrimary} />}
/>
</AntdColorPicker>
);
};

export default ColorPicker;
9 changes: 3 additions & 6 deletions src/layouts/ConsoleLayout/index.less
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
@import '@/styles/vars';

.console-layout {
width: 100%;
height: 100%;
display: flex;
background-color: var(--layout-background-color);
}

.console-layout__left-side {
height: 100%;
background-color: @layoutBackgroundColor;
}

.console-layout__right-side {
Expand All @@ -17,15 +15,14 @@
width: 100%;
display: flex;
flex-direction: column;
background-color: @layoutBackgroundColor;
}

.console-layout__right-side-main {
flex: 1;
overflow: auto;
padding: 24px;
background-color: #fff;
background-color: var(--container-background-color);
overflow: auto;
border-radius: 10px;
margin: 0 0 @layoutGutter @layoutGutter;
margin: 0 0 var(--layout-gutter) var(--layout-gutter);
}
Loading

0 comments on commit 1d57456

Please sign in to comment.