From 0a5b7f4b95a4508b887e229bb09a6de828beb28f Mon Sep 17 00:00:00 2001 From: chensz Date: Mon, 31 Jul 2023 15:43:54 +0800 Subject: [PATCH] feat: change theme color --- src/app.tsx | 19 +++++++++++++++++- src/features/theme/theme.module.css | 14 +++++++++++++ src/features/theme/theme.tsx | 31 +++++++++++++++++++++++++++++ src/features/theme/themeSlice.ts | 28 ++++++++++++++++++++++++++ src/layouts/header.tsx | 10 ++++++---- src/store.ts | 4 +++- src/utils/functions.ts | 12 +++++++++++ 7 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 src/features/theme/theme.module.css create mode 100644 src/features/theme/theme.tsx create mode 100644 src/features/theme/themeSlice.ts create mode 100644 src/utils/functions.ts diff --git a/src/app.tsx b/src/app.tsx index 9d3819a..77c518c 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,10 +1,27 @@ import { RouterProvider, createBrowserRouter, createHashRouter } from 'react-router-dom' import { routes } from './routes' +import { ConfigProvider } from 'antd' +import { useAppSelector } from './store' +import { themeColor } from '@/features/theme/themeSlice' + export default function App() { const createdRoutes = import.meta.env.VITE_ROUTER_HISTORY === 'hash' ? createHashRouter(routes) : createBrowserRouter(routes) - return + + const primaryColor = useAppSelector(themeColor) + + return ( + + + + ) } diff --git a/src/features/theme/theme.module.css b/src/features/theme/theme.module.css new file mode 100644 index 0000000..4fc0640 --- /dev/null +++ b/src/features/theme/theme.module.css @@ -0,0 +1,14 @@ +.skin { + position: relative; + width: 32px; + height: 32px; +} + +.skin-input { + width: 32px; + position: absolute; + left: 0; + z-index: 1; + opacity: 0; + cursor: pointer; +} diff --git a/src/features/theme/theme.tsx b/src/features/theme/theme.tsx new file mode 100644 index 0000000..4e7c96f --- /dev/null +++ b/src/features/theme/theme.tsx @@ -0,0 +1,31 @@ +import { ChangeEvent } from 'react' +import { Button, Input } from 'antd' +import { SkinOutlined } from '@ant-design/icons' + +import { useAppSelector, useAppDispatch } from '@/store' +import { setColor, themeColor } from './themeSlice' + +import { debounce } from '@/utils/functions' + +import styles from './theme.module.css' + +export default function Theme() { + const primaryColor = useAppSelector(themeColor) + const dispatch = useAppDispatch() + + const changeMainColor = (e: ChangeEvent) => { + dispatch(setColor(e.target.value)) + } + + return ( +
+
+ ) +} diff --git a/src/features/theme/themeSlice.ts b/src/features/theme/themeSlice.ts new file mode 100644 index 0000000..54fcfb9 --- /dev/null +++ b/src/features/theme/themeSlice.ts @@ -0,0 +1,28 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import { RootState } from '@/store' + +export interface ThemeState { + themeColor: string +} + +// use redux-persist?https://github.com/rt2zz/redux-persist +const initialState: ThemeState = JSON.parse( + localStorage.getItem('theme') || '{"themeColor":"#247fff"}' +) + +export const themeSlice = createSlice({ + name: 'theme', + initialState, + reducers: { + setColor: (state, action: PayloadAction) => { + state.themeColor = action.payload + localStorage.setItem('theme', JSON.stringify({ themeColor: action.payload })) + } + } +}) + +export const { setColor } = themeSlice.actions + +export const themeColor = (state: RootState) => state.theme.themeColor + +export default themeSlice.reducer diff --git a/src/layouts/header.tsx b/src/layouts/header.tsx index 53780ae..8b30f0b 100644 --- a/src/layouts/header.tsx +++ b/src/layouts/header.tsx @@ -1,13 +1,14 @@ -import { Breadcrumb, Row, Button, Dropdown, MenuProps } from 'antd' +import { Breadcrumb, Row, Space, Button, Dropdown, MenuProps } from 'antd' import { Link, useLocation, useNavigate, Navigate, useLoaderData } from 'react-router-dom' import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons' +import Theme from '@/features/theme/theme' + import { breadcrumbNameMap } from '../routes' import { logout } from '../routes/auth' export default function AdminLayout() { const userInfo: any = useLoaderData() - console.log('userInfo', userInfo) const location = useLocation() const navigate = useNavigate() @@ -73,7 +74,8 @@ export default function AdminLayout() { - + + {userInfo?.user?.username} - + ) } diff --git a/src/store.ts b/src/store.ts index d6f1e34..41c6dd0 100644 --- a/src/store.ts +++ b/src/store.ts @@ -2,10 +2,12 @@ import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit' import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux' import counterReducer from './features/counter/counterSlice' +import themeReducer from './features/theme/themeSlice' export const store = configureStore({ reducer: { - counter: counterReducer + counter: counterReducer, + theme: themeReducer } }) diff --git a/src/utils/functions.ts b/src/utils/functions.ts new file mode 100644 index 0000000..b0ad367 --- /dev/null +++ b/src/utils/functions.ts @@ -0,0 +1,12 @@ +export function debounce any>(fn: T, delay: number): T { + let timer: ReturnType | null = null + return function (this: any, ...args: Parameters) { + if (timer !== null) { + clearTimeout(timer) + } + timer = setTimeout(() => { + fn.apply(this, args) + timer = null + }, delay) + } as T +}