使用vite+react+ts+Ant Design开发后台管理项目(三)

news2024/11/15 16:03:14

 前言


本文将引导开发者从零基础开始,运用vite、react、react-router、react-redux、Ant Design、less、tailwindcss、axios等前沿技术栈,构建一个高效、响应式的后台管理系统。通过详细的步骤和实践指导,文章旨在为开发者揭示如何利用这些技术工具,从项目构思到最终实现的全过程,提供清晰的开发思路和实用的技术应用技巧。

 项目gitee地址:lbking666666/enqi-admin

  本系列文章:

  • 使用vite+react+ts+Ant Design开发后台管理项目(一)
  • 使用vite+react+ts+Ant Design开发后台管理项目(二)
  • 使用vite+react+ts+Ant Design开发后台管理项目(三)
  • 使用vite+react+ts+Ant Design开发后台管理项目(四)

添加配置

添加设置按钮

目前头部只有一个控制左侧菜单的按钮,需要在右侧添加一个按钮可以设置整体的一些配置。修改layout文件夹下的header.tsx文件

//layout/header.tsx
import React from "react";
import { Button, Layout, theme } from "antd";
import { MenuFoldOutlined, MenuUnfoldOutlined,SettingOutlined } from "@ant-design/icons";
const { Header } = Layout;
interface AppSiderProps {
  collapsed: boolean;
}
const AppHeader: React.FC<AppSiderProps> = ({ collapsed }) => {
  const {
    token: { colorBgContainer },
  } = theme.useToken();
  
  return (
    <Header style={{ padding: 0, background: colorBgContainer }}>
     <Flex gap="middle" justify="space-between" align="center">
        <Button
          type="text"
          icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
          style={{
            fontSize: "16px",
            width: 64,
            height: 64,
          }}
          onClick={handleCollapsed}
        />
        <Button
          type="primary"
          className="mr-4"
          icon={<SettingOutlined />}
        />
      </Flex>
    </Header>
  );
};
export default AppHeader;

添加Drawer抽屉

从Ant Design选择抽屉组件截图如下

把这里的代码拿到layout文件夹下的header.tsx文件中 

//layout/header.tsx

import React, { useState } from "react";
import { Button, Layout, theme, Flex, Drawer } from "antd";
import {
  MenuFoldOutlined,
  MenuUnfoldOutlined,
  SettingOutlined,
} from "@ant-design/icons";
import { useAppDispatch } from "@/hooks/UseGlobal.hooks";
import { setCollapsed } from "@/store/reducers/global";
const { Header } = Layout;
interface AppSiderProps {
  collapsed: boolean;
}

const AppHeader: React.FC<AppSiderProps> = ({ collapsed }) => {
  const {
    token: { colorBgContainer },
  } = theme.useToken();

  const [showPoup, setShowPoup] = useState(false);
  const dispatch = useAppDispatch();
  //收缩事件
  const handleCollapsed = () => {
    //更新全局状态  collapsed
    dispatch(setCollapsed());
  };
  //设置按钮点击事件
  const handleShowPoup = () => {
    console.log("点击了按钮");
    setShowPoup(true);
  };
  const onClose = () => {
    setShowPoup(false);
  };
  return (
    <Header style={{ padding: 0, background: colorBgContainer }}>
      <Flex gap="middle" justify="space-between" align="center">
        <Button
          type="text"
          icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
          style={{
            fontSize: "16px",
            width: 64,
            height: 64,
          }}
          onClick={handleCollapsed}
        />
        <Button
          type="primary"
          className="mr-4"
          icon={<SettingOutlined />}
          onClick={handleShowPoup}
        />
      </Flex>
      <Drawer title="Basic Drawer" onClose={onClose} open={showPoup}>
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Drawer>
    </Header>
  );
};
export default AppHeader;

查看效果发现关闭按钮在左侧一般我们习惯把关闭按钮放到右侧根据Ant Design的Drawer的api我们可以设置closeIcon和extra这两个

//layout/header.tsx
import React, { useState } from "react";
import { Button, Layout, theme, Flex, Drawer,Space } from "antd";
import {
  MenuFoldOutlined,
  MenuUnfoldOutlined,
  SettingOutlined,
  CloseOutlined
} from "@ant-design/icons";
import { useAppDispatch } from "@/hooks/UseGlobal.hooks";
import { setCollapsed } from "@/store/reducers/global";
const { Header } = Layout;
interface AppSiderProps {
  collapsed: boolean;
}

const AppHeader: React.FC<AppSiderProps> = ({ collapsed }) => {
  const {
    token: { colorBgContainer },
  } = theme.useToken();

  const [showPoup, setShowPoup] = useState(false);
  const dispatch = useAppDispatch();
  //收缩事件
  const handleCollapsed = () => {
    //更新全局状态  collapsed
    dispatch(setCollapsed());
  };
  //设置按钮点击事件
  const handleShowPoup = () => {
    console.log("点击了按钮");
    setShowPoup(true);
  };
  const onClose = () => {
    setShowPoup(false);
  };
  return (
    <Header style={{ padding: 0, background: colorBgContainer }}>
      <Flex gap="middle" justify="space-between" align="center">
        <Button
          type="text"
          icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
          style={{
            fontSize: "16px",
            width: 64,
            height: 64,
          }}
          onClick={handleCollapsed}
        />
        <Button
          type="primary"
          className="mr-4"
          icon={<SettingOutlined />}
          onClick={handleShowPoup}
        />
      </Flex>
      <Drawer
        title="设置"
        closeIcon={false}
        open={showPoup}
        extra={
          <Space>
            <Button type="link" onClick={onClose} icon={<CloseOutlined />}></Button>
          </Space>
        }
      ></Drawer>
    </Header>
  );
};
export default AppHeader;

效果如下图这里关闭按钮在右侧了

添加设置项

这里的查看Ant Design官网后,选择了三项设置,主题颜色的配置,暗黑模式、圆角模式

先把这三个内容添加进来,控制这三个的响应式变量先使用useState在头部组件中定义出来等调试好之后再更换为redux状态管理

//layout/header.tsx
import React, { useState } from "react";
import { Button, Layout, theme, Flex, Drawer, Space, Switch } from "antd";
import {
  MenuFoldOutlined,
  MenuUnfoldOutlined,
  SettingOutlined,
  CloseOutlined,
  CheckOutlined,
} from "@ant-design/icons";
import { useAppDispatch } from "@/hooks/UseGlobal.hooks";
import { setCollapsed } from "@/store/reducers/global";
const { Header } = Layout;
interface AppSiderProps {
  collapsed: boolean;
}
const colors = [
  {
    name: "拂晓蓝",
    value: "#1677ff",
  },
  {
    name: "薄暮",
    value: "#5f80c7",
  },
  {
    name: "日暮",
    value: "#faad14",
  },
  {
    name: "火山",
    value: "#f5686f",
  },
  {
    name: "酱紫",
    value: "#9266f9",
  },
  {
    name: "极光绿",
    value: "#3c9",
  },
  {
    name: "极客蓝",
    value: "#32a2d4",
  },
];

const AppHeader: React.FC<AppSiderProps> = ({ collapsed }) => {
  const {
    token: { colorBgContainer },
  } = theme.useToken();

  //抽屉弹出
  const [showPoup, setShowPoup] = useState(false);
  const [curColor, setCurColor] = useState("#1677ff");
  const [isSelectdDark, setIsSelectdDark] = useState(false);
  const [isSelectdRadius, setIsSelectdRadius] = useState(false);
  const onChangeDark = (checked: boolean) => {
    setIsSelectdDark(checked);
  };
  const onChangeRadius = (checked: boolean) => {
    setIsSelectdRadius(checked);
  };
  const handlesetCurColor = (color: string) => {
    setCurColor(color);
  };
  const ColorItem: React.FC<{ color: string; isSelectd: boolean }> = ({
    color,
    isSelectd,
  }) => {
    if (isSelectd) {
      return (
        <div
          className="w-6 h-6 flex justify-center items-center  rounded cursor-pointer items"
          style={{ background: color }}
        >
          <CheckOutlined style={{ color: "#fff" }} />
        </div>
      );
    } else {
      return (
        <div
          className="w-6 h-6 flex justify-center items-center  rounded cursor-pointer items"
          style={{ background: color }}
          onClick={() => handlesetCurColor(color)}
        ></div>
      );
    }
  };
  const dispatch = useAppDispatch();
  //收缩事件
  const handleCollapsed = () => {
    //更新全局状态  collapsed
    dispatch(setCollapsed());
  };
  //设置按钮点击事件
  const handleShowPoup = () => {
    setShowPoup(true);
  };
  const onClose = () => {
    setShowPoup(false);
  };
  return (
    <Header style={{ padding: 0, background: colorBgContainer }}>
      <Flex gap="middle" justify="space-between" align="center">
        <Button
          type="text"
          icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
          style={{
            fontSize: "16px",
            width: 64,
            height: 64,
          }}
          onClick={handleCollapsed}
        />
        <Button
          type="primary"
          className="mr-4"
          icon={<SettingOutlined />}
          onClick={handleShowPoup}
        />
      </Flex>
      <Drawer
        title="设置"
        width={300}
        closeIcon={false}
        open={showPoup}
        extra={
          <Space>
            <Button
              type="text"
              onClick={onClose}
              icon={<CloseOutlined />}
            ></Button>
          </Space>
        }
      >
        <div className="mb-3 font-bold">主题颜色</div>
        <Flex gap="middle" justify="space-between" align="center">
          {colors.map((item) => (
            <ColorItem
              key={item.value}
              color={item.value}
              isSelectd={curColor == item.value}
            />
          ))}
        </Flex>
        <div className="mb-3 mt-3 font-bold">主题模式</div>
        <div className="flex justify-between">
          <div className="flex gap-2">
            <span>开启暗黑模式</span>
          </div>
          <div className="flex gap-2">
            <Switch defaultChecked checked={isSelectdDark} onChange={onChangeDark} />
          </div>

        </div>
        <div className="flex justify-between">
          <div className="flex gap-2">
            <span>开启圆角主题</span>
          </div>
          <div className="flex gap-2">
            <Switch defaultChecked checked={isSelectdRadius} onChange={onChangeRadius} />
          </div>

        </div>
      </Drawer>
    </Header>
  );
};
export default AppHeader;

此时查看效果如下图,修改主题颜色、暗黑模式、圆角模式都可以得到对应的效果

改造global.ts

把三个设置项和设置drawer显隐状态抽离到之前的global.ts文件中

//store/reducers/global.ts
import { createSlice } from "@reduxjs/toolkit";
import type { RootState } from "@/store/index.ts";

// 定义初始 state 的类型
interface GlobalState {
  collapsed: boolean;//是否折叠
  showSetting: boolean;//是否显示设置
  colorPrimary: string;//主题颜色
  isDark: boolean;//是否暗黑模式
  isRadius:boolean;//是否圆角
}
// 使用该类型定义初始 state
const initialState: GlobalState = {
  collapsed: false,
  showSetting: false,
  colorPrimary: "#1677ff",
  isDark: false,
  isRadius:true
};
// 创建 slice
export const globalSlice = createSlice({
  name: "global", // 名称
  initialState, // 初始 state
  reducers: {
    // 定义 reducer 函数,该函数接受 state 和 action 作为参数
    setCollapsed: (state) => {
      // 更新 state
      state.collapsed = !state.collapsed;
    },
    setShowSetting: (state,action) => {
      // 更新设置状态为 action 载荷
      state.showSetting = action.payload;
    },
    setIsDark: (state) => {
      // 更新暗黑模式状态
      state.isDark = !state.isDark;
    },
    setColorPrimary: (state, action) => {
      // 更新主题颜色为 action 载荷
      state.colorPrimary = action.payload;
    },
    setIsRadius: (state) => {
      // 更新圆角状态
      state.isRadius = !state.isRadius;
    },
  },
});

// 为每个 case reducer 函数生成 Action creators
export const { setCollapsed, setIsDark, setColorPrimary,setShowSetting,setIsRadius } =
  globalSlice.actions;
// selectors 等其他代码可以使用导入的 `RootState` 类型
export const selectCollapsed = (state: RootState) => state.global.collapsed;
export const selectShowSetting = (state: RootState) => state.global.showSetting;
export const selectColorPrimary = (state: RootState) =>
  state.global.colorPrimary;
export const selectIsDark = (state: RootState) => state.global.isDark;
export const selectIsRadius = (state: RootState) => state.global.isRadius;
// 导出 reducer
export default globalSlice.reducer;

添加设置项组件

把设置项drawer的代码抽离到单独的组件中,在layout文件夹下新增setting.tsx

//layout/setting.tsx
import React, { useState } from "react";
import { Button, Flex, Drawer, Space, Switch } from "antd";
import { CloseOutlined, CheckOutlined } from "@ant-design/icons";
const colors = [
  {
    name: "拂晓蓝",
    value: "#1677ff",
  },
  {
    name: "薄暮",
    value: "#5f80c7",
  },
  {
    name: "日暮",
    value: "#faad14",
  },
  {
    name: "火山",
    value: "#f5686f",
  },
  {
    name: "酱紫",
    value: "#9266f9",
  },
  {
    name: "极光绿",
    value: "#3c9",
  },
  {
    name: "极客蓝",
    value: "#32a2d4",
  },
];
const Setting = () => {
  const [showPoup, setShowPoup] = useState(false);
  const [curColor, setCurColor] = useState("#1677ff");
  const [isSelectdDark, setIsSelectdDark] = useState(false);
  const [isSelectdRadius, setIsSelectdRadius] = useState(false);
  const onChangeDark = (checked: boolean) => {
    setIsSelectdDark(checked);
  };
  const onChangeRadius = (checked: boolean) => {
    setIsSelectdRadius(checked);
  };
  const handlesetCurColor = (color: string) => {
    setCurColor(color);
  };
  
  const onClose = () => {
    setShowPoup(false);
  };
  const ColorItem: React.FC<{ color: string; isSelectd: boolean }> = ({
    color,
    isSelectd,
  }) => {
    if (isSelectd) {
      return (
        <div
          className="w-6 h-6 flex justify-center items-center  rounded cursor-pointer items"
          style={{ background: color }}
        >
          <CheckOutlined style={{ color: "#fff" }} />
        </div>
      );
    } else {
      return (
        <div
          className="w-6 h-6 flex justify-center items-center  rounded cursor-pointer items"
          style={{ background: color }}
          onClick={() => handlesetCurColor(color)}
        ></div>
      );
    }
  };
  return (
    <Drawer
      title="设置"
      width={300}
      closeIcon={false}
      open={showPoup}
      extra={
        <Space>
          <Button
            type="text"
            onClick={onClose}
            icon={<CloseOutlined />}
          ></Button>
        </Space>
      }
    >
      <div className="mb-3 font-bold">主题颜色</div>
      <Flex gap="middle" justify="space-between" align="center">
        {colors.map((item) => (
          <ColorItem
            key={item.value}
            color={item.value}
            isSelectd={curColor == item.value}
          />
        ))}
      </Flex>
      <div className="mb-3 mt-3 font-bold">主题模式</div>
      <div className="flex justify-between mb-3">
        <div className="flex gap-2">
          <span>开启暗黑模式</span>
        </div>
        <div className="flex gap-2">
          <Switch
            defaultChecked
            checked={isSelectdDark}
            onChange={onChangeDark}
          />
        </div>
      </div>
      <div className="flex justify-between">
        <div className="flex gap-2">
          <span>开启圆角主题</span>
        </div>
        <div className="flex gap-2">
          <Switch
            defaultChecked
            checked={isSelectdRadius}
            onChange={onChangeRadius}
          />
        </div>
      </div>
    </Drawer>
  );
};

export default Setting;

组件中应用redux状态

头部组件

//laout/header.tsx
import React, { useState } from "react";
import { Button, Flex, Drawer, Space, Switch } from "antd";
import { CloseOutlined, CheckOutlined } from "@ant-design/icons";
const colors = [
  {
    name: "拂晓蓝",
    value: "#1677ff",
  },
  {
    name: "薄暮",
    value: "#5f80c7",
  },
  {
    name: "日暮",
    value: "#faad14",
  },
  {
    name: "火山",
    value: "#f5686f",
  },
  {
    name: "酱紫",
    value: "#9266f9",
  },
  {
    name: "极光绿",
    value: "#3c9",
  },
  {
    name: "极客蓝",
    value: "#32a2d4",
  },
];
const Setting = () => {
  const [showPoup, setShowPoup] = useState(false);
  const [curColor, setCurColor] = useState("#1677ff");
  const [isSelectdDark, setIsSelectdDark] = useState(false);
  const [isSelectdRadius, setIsSelectdRadius] = useState(false);
  const onChangeDark = (checked: boolean) => {
    setIsSelectdDark(checked);
  };
  const onChangeRadius = (checked: boolean) => {
    setIsSelectdRadius(checked);
  };
  const handlesetCurColor = (color: string) => {
    setCurColor(color);
  };
  
  const onClose = () => {
    setShowPoup(false);
  };
  const ColorItem: React.FC<{ color: string; isSelectd: boolean }> = ({
    color,
    isSelectd,
  }) => {
    if (isSelectd) {
      return (
        <div
          className="w-6 h-6 flex justify-center items-center  rounded cursor-pointer items"
          style={{ background: color }}
        >
          <CheckOutlined style={{ color: "#fff" }} />
        </div>
      );
    } else {
      return (
        <div
          className="w-6 h-6 flex justify-center items-center  rounded cursor-pointer items"
          style={{ background: color }}
          onClick={() => handlesetCurColor(color)}
        ></div>
      );
    }
  };
  return (
    <Drawer
      title="设置"
      width={300}
      closeIcon={false}
      open={showPoup}
      extra={
        <Space>
          <Button
            type="text"
            onClick={onClose}
            icon={<CloseOutlined />}
          ></Button>
        </Space>
      }
    >
      <div className="mb-3 font-bold">主题颜色</div>
      <Flex gap="middle" justify="space-between" align="center">
        {colors.map((item) => (
          <ColorItem
            key={item.value}
            color={item.value}
            isSelectd={curColor == item.value}
          />
        ))}
      </Flex>
      <div className="mb-3 mt-3 font-bold">主题模式</div>
      <div className="flex justify-between mb-3">
        <div className="flex gap-2">
          <span>开启暗黑模式</span>
        </div>
        <div className="flex gap-2">
          <Switch
            defaultChecked
            checked={isSelectdDark}
            onChange={onChangeDark}
          />
        </div>
      </div>
      <div className="flex justify-between">
        <div className="flex gap-2">
          <span>开启圆角主题</span>
        </div>
        <div className="flex gap-2">
          <Switch
            defaultChecked
            checked={isSelectdRadius}
            onChange={onChangeRadius}
          />
        </div>
      </div>
    </Drawer>
  );
};

export default Setting;

设置组件

//layout/setting.tsx
import React from "react";
import { Button, Flex, Drawer, Space, Switch } from "antd";
import { CloseOutlined, CheckOutlined } from "@ant-design/icons";
import { useAppSelector,useAppDispatch } from "@/hooks/UseGlobal.hooks";
import {  selectColorPrimary,selectIsDark,selectIsRadius,setIsDark, setColorPrimary,setShowSetting,setIsRadius } from "@/store/reducers/global";
const colors = [
  {
    name: "拂晓蓝",
    value: "#1677ff",
  },
  {
    name: "薄暮",
    value: "#5f80c7",
  },
  {
    name: "日暮",
    value: "#faad14",
  },
  {
    name: "火山",
    value: "#f5686f",
  },
  {
    name: "酱紫",
    value: "#9266f9",
  },
  {
    name: "极光绿",
    value: "#3c9",
  },
  {
    name: "极客蓝",
    value: "#32a2d4",
  },
];
type AppSiderProps = {
  showPoup: boolean;
}
const Setting:React.FC<AppSiderProps> = ({showPoup}) => {
  const curColor = useAppSelector(selectColorPrimary);
  const isSelectdDark = useAppSelector(selectIsDark);
  const isSelectdRadius = useAppSelector(selectIsRadius);
  const dispatch = useAppDispatch();
  const onChangeDark = () => {
    dispatch(setIsDark());
  };
  const onChangeRadius = () => {
    dispatch(setIsRadius());
  };
  const handlesetCurColor = (color: string) => {
    dispatch(setColorPrimary(color));
  };
  
  const onClose = () => {
    dispatch(setShowSetting(false));
  };
  const ColorItem: React.FC<{ color: string; isSelectd: boolean }> = ({
    color,
    isSelectd,
  }) => {
    if (isSelectd) {
      return (
        <div
          className="w-6 h-6 flex justify-center items-center  rounded cursor-pointer items"
          style={{ background: color }}
        >
          <CheckOutlined style={{ color: "#fff" }} />
        </div>
      );
    } else {
      return (
        <div
          className="w-6 h-6 flex justify-center items-center  rounded cursor-pointer items"
          style={{ background: color }}
          onClick={() => handlesetCurColor(color)}
        ></div>
      );
    }
  };
  return (
    <Drawer
      title="设置"
      width={300}
      closeIcon={false}
      open={showPoup}
      extra={
        <Space>
          <Button
            type="text"
            onClick={onClose}
            icon={<CloseOutlined />}
          ></Button>
        </Space>
      }
    >
      <div className="mb-3 font-bold">主题颜色</div>
      <Flex gap="middle" justify="space-between" align="center">
        {colors.map((item) => (
          <ColorItem
            key={item.value}
            color={item.value}
            isSelectd={curColor == item.value}
          />
        ))}
      </Flex>
      <div className="mb-3 mt-3 font-bold">主题模式</div>
      <div className="flex justify-between mb-3">
        <div className="flex gap-2">
          <span>开启暗黑模式</span>
        </div>
        <div className="flex gap-2">
          <Switch
            defaultChecked
            checked={isSelectdDark}
            onChange={onChangeDark}
          />
        </div>
      </div>
      <div className="flex justify-between">
        <div className="flex gap-2">
          <span>开启圆角主题</span>
        </div>
        <div className="flex gap-2">
          <Switch
            defaultChecked
            checked={isSelectdRadius}
            onChange={onChangeRadius}
          />
        </div>
      </div>
    </Drawer>
  );
};

export default Setting;

本地存储

设置了不同的主题和是否选择暗黑模式、圆角模式后我们希望使用者在下次进入到系统时候能继续使用之前的选择,这时我们需要对global.ts的状态管理做一些改造,把状态存储到本地存储中

//store/reducers/global.ts
import { createSlice } from "@reduxjs/toolkit";
import type { RootState } from "@/store/index.ts";

// 定义初始 state 的类型
interface GlobalState {
  collapsed: boolean; //是否折叠
  showSetting: boolean; //是否显示设置
  colorPrimary: string; //主题颜色
  isDark: boolean; //是否暗黑模式
  isRadius: boolean; //是否圆角
}
const getLocal = (type:string) => {
  //获取本地存储
  return localStorage.getItem(type);
};
// 使用该类型定义初始 state
const initialState: GlobalState = {
  collapsed: getLocal("collapsed")==='true' || false,
  showSetting: false,
  colorPrimary: getLocal("colorPrimary") || "#1677ff",
  isDark: getLocal("isDark") ==='true' || false,
  isRadius: getLocal("isRadius")!=='false' || true,
};
const setLocal = (type:string,value: string | boolean)=>{
  if (typeof value === 'string') {
    localStorage.setItem(type, value);
} else {
    // 可以选择忽略非字符串值,或者进行转换处理
    // 例如,将布尔值转换为字符串
    localStorage.setItem(type, value.toString());
}
}
// 创建 slice
export const globalSlice = createSlice({
  name: "global", // 名称
  initialState, // 初始 state
  reducers: {
    // 定义 reducer 函数,该函数接受 state 和 action 作为参数
    setCollapsed: (state) => {
      // 更新 state
      state.collapsed = !state.collapsed;
      setLocal("collapsed",state.collapsed);
    },
    setShowSetting: (state, action) => {
      // 更新设置状态为 action 载荷
      state.showSetting = action.payload;
    },
    setIsDark: (state) => {
      // 更新暗黑模式状态
      state.isDark = !state.isDark;
      setLocal("isDark",state.isDark);
    },
    setColorPrimary: (state, action) => {
      // 更新主题颜色为 action 载荷
      state.colorPrimary = action.payload;
      setLocal('colorPrimary',action.payload);
    },
    setIsRadius: (state) => {
      // 更新圆角状态
      state.isRadius = !state.isRadius;
      setLocal("isRadius",state.isRadius);
    },
  },
});

// 为每个 case reducer 函数生成 Action creators
export const {
  setCollapsed,
  setIsDark,
  setColorPrimary,
  setShowSetting,
  setIsRadius,
} = globalSlice.actions;
// selectors 等其他代码可以使用导入的 `RootState` 类型
export const selectCollapsed = (state: RootState) => state.global.collapsed;
export const selectShowSetting = (state: RootState) => state.global.showSetting;
export const selectColorPrimary = (state: RootState) =>
  state.global.colorPrimary;
export const selectIsDark = (state: RootState) => state.global.isDark;
export const selectIsRadius = (state: RootState) => state.global.isRadius;
// 导出 reducer
export default globalSlice.reducer;

此时我们选择不同的设置

刷新页面后可以看到就是之前设置好的配置

总结

到此我们对全局配置的一个大概的工作完成了,当然实际项目和需求中可能会有不同或更多的配置按照这个思路可以添加不同的配置,当然如果已经很熟练的使用react+redux可以直接省略掉很多中间的步骤 ,比如直接新建setting.tsx子组件写逻辑,和直接在global.ts中添加状态管理及方法在组件中直接应用。

后续

本篇文章为项目使用redux配合本地存储做了全局设置,代码已经同步到了gitee仓库,下一篇会使用axios和mock来设置左侧菜单

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2165648.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用AI进行需求分析的案例研究

生成式 AI 的潜在应用场景似乎无穷无尽。虽然这令人兴奋&#xff0c;但也可能让人不知所措。因此&#xff0c;团队在使用这项技术时需要有明确的目标&#xff1a;关键是要明确生成式 AI 在团队工作中能产生哪些实质性影响。 在软件工程中&#xff0c;一个引人注目的应用场景是…

QMT如何获取股票基本信息?如上市时间、退市时间、代码、名称、是否是ST等。QMT量化软件支持!

获取股票概况 包含股票的上市时间、退市时间、代码、名称、是否是ST等。 #获取合约基础信息数据 该信息每交易日9点更新 #内置Python 提示 旧版本客户端中&#xff0c;函数名为ContextInfo.get_instrumentdetail 调用方法 内置python ContextInfo.get_instrument_detai…

全连接神经网络

这里写目录标题 全连接神经网络vs前馈神经网络基于全连接神经网络的手写数字识别使用Pytorch实现纯Python实现 全连接神经网络的局限 端到端学习 深度学习有时也称为端到端机器学习&#xff08;end-to-end machine learning&#xff09;。这里所说的端到端是指从一端到另一端的…

西门子因为TC在与PTC及达索的混战中占据优势,西门子与SAP的合作价值几何?(2)

今天这篇文章是通过腾讯会议先录了一个视频然后转录的&#xff0c;看看效果怎么样。 主要是我昨天写了一篇公众号的文章&#xff0c;这篇文章是转自国外的一个记者。写的是他对这个工业软件三巨头西门子、达索和PTC之间的竞争做的一个访谈性质的文章&#xff0c;谈了他的一些看…

1.1.5 计算机网络的性能指标(上)

信道&#xff1a; 表示向某一方向传送信息的通道&#xff08;信道&#xff01;通信线路&#xff09;一条通信线路在逻辑上往往对应一条发送信道和一条接收信道。 速率&#xff1a; 指连接到网络上的节点在信道上传输数据的速率。也称数据率或比特率、数据传输速率。 速率单…

python常见的魔术方法

什么是魔术方法 Python类的内置方法&#xff0c;各自有各自的特殊功能&#xff0c;被称之为魔术方法 常见的魔术方法有以下&#xff1a; __init__:构造方法 __str__:字符串方法 __lt__:小于、大于符号比较 __le__:小于等于、大于等于符合比较 __eq__:等于符合比较__init__ c…

【论文速看】DL最新进展20240925-医学图像分割、目标跟踪、图像超分

目录 【医学图像分割】【目标跟踪】【图像超分】 【医学图像分割】 [2024] UU-Mamba: Uncertainty-aware U-Mamba for Cardiovascular Segmentation 论文链接&#xff1a;https://arxiv.org/pdf/2409.14305 代码链接&#xff1a;https://github.com/tiffany9056/UU-Mamba 在深…

【NLP】循环神经网络--RNN学习.day3

一.初步认识RNN 循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;是一种用于处理序列数据的深度学习模型。与传统的静态神经网络相比&#xff0c;RNN 可以有效处理输入数据的时间序列特性。这使得 RNN 在处理自然语言处理&#xff08;NLP&#xff09;、时间…

【Python报错已解决】TypeError: forward() got an unexpected keyword argument ‘labels‘

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

使用API有效率地管理Dynadot域名,注册域名服务器(NS)信息

前言 Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮箱&…

【9.模块化开发和代码重用之——头文件、动静态库】

目录 前言软件工程的基本原则程序的模块化开发和代码重用技术开发自己的头文件定义实现自己的头文件编写实现文件&#xff08;源文件&#xff09;编译代码链接目标文件到可执行文件 实现类似标准库效果的几种方法实际使用的开发方法头文件库 尝试自动链接静态库&#xff08;好像…

替换jar包中class文件

虽然.jar文件是一种Java归档文件&#xff0c;可以使用压缩软件打开&#xff0c;但是并不能通过压缩软件来修改其内容&#xff0c;只能通过jar命令来更新文件。 一、背景 在使用血氧仪SDK时出现了问题&#xff0c;经过分析是在申请权限时版本不兼容导致的闪退异常&#xff0c;…

大数据新视界 --大数据大厂之 Kylin 多维分析引擎实战:构建数据立方体

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

全国信息学奥赛泄题事件:一场对公平与公正的严峻考验

在科技与教育日益融合的今天&#xff0c;信息学奥林匹克竞赛作为选拔计算机科学领域未来人才的重要平台&#xff0c;始终承载着培养青少年逻辑思维、编程能力和创新潜力的重任。然而&#xff0c;2024年9月发生的一起全国CSP-J/S认证考试泄题事件&#xff0c;却如同一枚重磅炸弹…

SpringBoot教程(三十) | SpringBoot集成Shiro权限框架

SpringBoot教程&#xff08;三十&#xff09; | SpringBoot集成Shiro权限框架 一、 什么是Shiro二、Shiro 组件核心组件其他组件 三、流程说明shiro的运行流程 四、SpringBoot 集成 Shiro &#xff08;shiro-spring-boot-web-starter方式&#xff09;1. 添加 Shiro 相关 maven2…

领导让部署一个系统服务,我该怎么弄?

文章目录 Dockerdocker基础通过Dockerfile构建镜像打包镜像&#xff0c;离线使用docker修改代理内容 Nginxubuntu安装nginxnginx基本操作 问题Sqlalchemy可以反射一些表&#xff0c;另外一些反射为None查看服务器的架构和版本&#xff0c;查看本机外网IPPG数据库创建角色创建库…

CentOS7 离线部署docker和docker-compose环境

一、Docker 离线安装 1. 下载docker tar.gz包 下载地址&#xff1a; Index of linux/static/stable/x86_64/ 本文选择版本&#xff1a;23.0.6 2.创建docker.service文件 vi docker.service文件内容如下&#xff1a; [Unit] DescriptionDocker Application Container Engi…

SpringBoot中XXL-JOB实现灵活控制的分片处理方案

❃博主首页 &#xff1a; 「码到三十五」 &#xff0c;同名公众号 :「码到三十五」&#xff0c;wx号 : 「liwu0213」 ☠博主专栏 &#xff1a; <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 &#xff1a…

9.25盒马鲜生一面

1.自我介绍 2.css两种盒子模型 ​3.rem和em 4.px概念 5.transition和animation的区别 6.移动端适配方案 7.vh、vw、% 8.js基本数据类型 9.call、apply、bind的区别 10.js实现继承的方法 11.get和post的区别 12.web安全&#xff08;XSS&#xff0c;CSRF&#xff09; …

甩锅笔记:好好的服务端应用突然起不来,经定位是无法访问外网了?测试又说没改网络配置,该如何定位?

在工作中、团队协作时&#xff0c;可能遇到的问题&#xff0c;如集成测试等场景。但是作为偏前端的全栈&#xff0c;锅从天上来&#xff0c;不是你想甩就能甩&#xff0c;尤其面对测试等比较强势的团体&#xff08;bug创造者&#xff09;&#xff0c;你必须有强大的心理承受能力…