react中台框架之antd

about

react生态中有很多框架,其中antd是比较非常不错的。 在工作中常用它做一些管理工作。

Ant Design是阿里蚂蚁金服团队基于React开发的ui组件,主要用于中后台系统的使用。

本文基于react+antd做一个中台框架demo.

  • 不同的顶部菜单,产生不同的侧边菜单。动态生成
  • 侧边菜单的内容显示
  • 侧边手工收起
  • 顶层menu自适应

效果

请添加图片描述

项目目录

# tree app
app
├── node_modules/
├── package.json
├── src
│   ├── index.js          //主入口文件
│   ├── routers.js        //主路由配置
│   ├── assets
│   │   ├── logo.scss     //图标样式(动画样式)
│   │   └── logo.png      //图标
│   └── test
│       ├── test.tsx      //主框架
│       ├── login.js      //在框架外显示示例
│       ├── p_sider.tsx   //侧边菜单生成
│       └── p_content.js  //侧边菜单子菜单内容
├── tsconfig.json
├── typings.d.ts
└── yarn.lock

源文件

主框架

test.tsx

import React from 'react';
import { useNavigate,Outlet } from "react-router-dom";

import { 
  Layout, 
  Menu, 
  theme,
  MenuProps,
  Flex,
 } from 'antd';

import {
  UserOutlined,
  MailOutlined,
  SettingOutlined,
  AppstoreOutlined,
} from '@ant-design/icons';

import '../assets/logo.scss';

const logoPNG = require('../assets/logo.png');
const { Header, Footer } = Layout;


//----------css样式配置-------------------------------
//layout样式
const layoutStyle = {
  overflow: 'hidden',
  minHeight: 'calc(100vh - 16px)',  //此配置,可以让则边导航全高度自适应。
};
//header样式
const headerStyle = {
  overflow: 'hidden',
  width: '100vw',                  //宽度默认自适应
  minWidth:0,
  display: 'flex', 
  alignItems: 'center',
  //backgroundColor: '#0096ff',
  //border: '5px dotted red',
  'padding-left':'0px',
  'padding-right':'0px',
  'justifyContent':'space-between',
};
//顶部导航样式
const topMenuStyle ={
  //flex: 'flex-shrink',
  display: 'flex', 
  width:'inherit',
  minWidth:0,
  'padding-right':'25px',
  //backgroundColor: '#0096ff',
  //border: '5px dotted red',
  'justifyContent':'flex-end',
  "backgroundColor": 'inherit',
}
//title文本样式
const webTileStyleText ={
  "color":"#fff",
  "fontFamily":"-moz-initial",
  "backgroundColor": 'inherit',
}

//----------menu配置-------------------------------
//顶部menu
const topMenuItems: MenuProps['items'] = [
    {
      label: 'about',
      key: 'about',
      icon: <MailOutlined />,
    },
    {
      label: 'menu1',
      key: 'menu1',
      icon: <MailOutlined />,
    },
    {
      label: 'menu2',
      key: 'menu2',
      icon: <SettingOutlined />,
      children: [
        {
          label: '菜单1',
          key: 'menu21',
          icon: <MailOutlined />,
        },
        {
          label: '菜单2',
          key: 'menu22',
          icon: <MailOutlined />,
        },
      ],
    },
    {
      label: 'sider1',
      key: 'sider1',
      icon: <AppstoreOutlined />,
    },
    {
      label: 'sider2',
      key: 'sider2',
      icon: <AppstoreOutlined />,
    },
    {
        label: 'sider3',
        key: 'sider3',
        icon: <AppstoreOutlined />,
      },
    {
      label: 'login',
      key: 'login',
      icon: <UserOutlined />,
    },
  ];

//主程序
const App: React.FC = () => {
  //调用theme的useToken()获取当前主题下的Design Token
  const {token}=theme.useToken()
  const colorBgContainer = token.colorBgContainer
  const borderRadiusLG = token.borderRadiusLG

  //顶部导航click事件
  const navigate = useNavigate();
  const onMenuClick: MenuProps['onClick'] = (e) => {
    console.log('click ', e.key);
    switch (e.key) {
    case "about":
      navigate("/admin/about");
      break;
    case "menu1":
      navigate("/admin/menu1");
      break;
    case "menu21":
      navigate("/admin/menu21");
      break;
    case "menu22":
      navigate("/admin/menu22");
      break;
    case "sider1":
      navigate("/admin/sider/sider1");
      break;
    case "sider2":
      navigate("/admin/sider/sider2");
      break;
    case "sider3":
      navigate("/admin/sider/sider3");
      break;
    case "login":
      navigate("/login");
      break;
    default:
      navigate("/");
      break;
    }
  };
  
  return (
    <Layout style={layoutStyle}>
      {/* header配置 */}
      <Header style={headerStyle}>
        <a href="/"><Flex style={webTileStyleText}> <img src={logoPNG} className="App-logo" />MyWebSiteName</Flex></a>
        <Menu
          selectable={true}
          mode="horizontal"
          defaultSelectedKeys={['about']}
          items={topMenuItems}
          style={topMenuStyle}
          onClick={onMenuClick}
        />
      </Header>

      {/* 显示区配置 */}
      <Layout style={{ color: '#fff'}}>
        <Outlet />
      </Layout>

      {/* 页脚配置 */}
      <Footer style={{ textAlign: 'center'}}>
          <div style={{ color:'red',display: 'inline'}}>Test</div> ©{new Date().getFullYear()} Created by guo-fs.com
      </Footer>
    </Layout>
  );
};

export default App;

侧边菜单动态生成

p_sider.tsx

import React, {useEffect,useState } from 'react'; 
import { useNavigate,Outlet } from 'react-router-dom'
import { useLocation } from 'react-router-dom';

import {
    MailOutlined,
    RightOutlined,
    LeftOutlined,
    PieChartOutlined,
    DesktopOutlined,
    UserOutlined,
    TeamOutlined,
    FileOutlined,
    HomeOutlined,
} from '@ant-design/icons';

import { 
  Layout, 
  Menu, 
  MenuProps,
  Flex,
  Button,
} from 'antd';

const {Sider, Content } = Layout;


//侧边sider1
const itemsSider1: MenuProps['items']  = [
    {
      label: 'Sider1-1',
      key: 'sider1-1',
      icon: <HomeOutlined />,
    },
    {
      label: 'Sider1-2',
      key: 'sider1-2',
      icon: <MailOutlined />,
    },
    {
      label: 'Sider1-3',
      key: 'sider1-3',
      icon: <MailOutlined />,
    },
]
//侧边sider2
const itemsSider2: MenuProps['items']  = [
    {
      label: 'Sider2-1',
      key: 'sider2-1',
      icon: <MailOutlined />,
    },
    {
      label: 'Sider2-2',
      key: 'sider2-2',
      icon: <MailOutlined />,
    },
    {
      label: 'Sider2-3',
      key: 'sider2-3',
      icon: <MailOutlined />,
      children: [
        {
          label: 'Sider23-1',
          key: 'sider23-1',
          icon: <MailOutlined />,
        },
        {
          label: 'Sider23-2',
          key: 'sider23-2',
          icon: <MailOutlined />,
        },
      ]
    },
    {
      label: 'Sider2-4',
      key: 'sider2-4',
      icon: <MailOutlined />,
    },
    {
      label: 'Sider2-5',
      key: 'sider2-5',
      icon: <MailOutlined />,
    },
]
//侧边sider3
type MenuItem = Required<MenuProps>['items'][number];
function getItem(
  label: React.ReactNode,
  key: React.Key,
  icon?: React.ReactNode,
  children?: MenuItem[],
): MenuItem {
  return {
    key,
    icon,
    children,
    label,
  } as MenuItem;
}
const itemsSider3: MenuItem[] = [
  getItem('Option 1', '1', <PieChartOutlined />),
  getItem('Option 2', '2', <DesktopOutlined />),
  getItem('User', 'sub1', <UserOutlined />, [
    getItem('Tom', '3',<UserOutlined />),
    getItem('Bill', '4'),
    getItem('Alex', '5'),
  ]),
  getItem('Team', 'sub2', <TeamOutlined />, [getItem('Team 1', '6'), getItem('Team 2', '8')]),
  getItem('Files', '9', <FileOutlined />),
];

//flex样式,用于siderMenu中的配置,底部显示折叠按扭
const flexStyle = {
  backgroundColor: 'inherit',
  height: 'inherit',
}


//动态生成侧边menu
const SiderMenu = () => {
  //页面侧边menu单击事件。所有侧边menu的单击事件都在此配置
  const navigate = useNavigate();
  const onMenuClick = (e:any) => {
      //console.log('click ', e.key);
      switch (e.key) {
          case "sider1-1":                     //菜单key          
            navigate("/admin/sider/sider1/menu1");   //跳转地址为路由中配置的url
            break;
          case "sider1-2":                      //菜单key             
            navigate("/admin/sider/sider1/menu11");   //跳转地址为路由中配置的url
            break;
          case "sider2-1":
            navigate("/admin/sider/sider2/menu2");            
            break;
          case "sider2-2":
            navigate("/admin/sider/sider2/menu21");            
            break;
      }
  }

  //当为侧边导航时,菜单收起配置
  const [collapsed, setCollapsed] = useState(false);

  //侧边导航折叠按扭配置(通过单击button实现)
  const [isClick, setIsClick] = useState(true);
  const [bticon, setBticon] = useState(<LeftOutlined />);
  const [btname, setBtname] = useState('收起');
  function onBtClick() {
    if (isClick) {
        setBticon(<RightOutlined />)
        //setBticon(<MenuUnfoldOutlined />)
        setBtname("显示")
        //setBtname("")
        setCollapsed(true)
        setIsClick(false)
    } else {
        setBticon(<LeftOutlined />)
        //setBticon(<MenuFoldOutlined />)
        setBtname("收起")
        //setBtname("")
        setIsClick(true)
        setCollapsed(false)
    }
  }

 
  //页面侧边menu动态配置。
  const location = useLocation()
  const siderUrl =location.pathname                  //提取当前页面url路径
  const [sidermenu, setSidermenu] = useState(null);  //sider菜单的状态变量
  useEffect(() => {
      const siderType = (siderUrl.split('/'))[3];   //提取url中第2个'/'后的字串
      //console.log("运行useEffect...,siderType:",siderType)
      var menuconf:any= null
      if (siderType == "sider1") {
        menuconf = <Menu mode="inline" items={itemsSider1} onClick={onMenuClick} />
        setSidermenu(menuconf)
      }
      if (siderType == "sider2") {
        menuconf = <Menu mode="inline" items={itemsSider2} onClick={onMenuClick} />
        setSidermenu(menuconf)
      }
      if (siderType == "sider3") {
        menuconf = <Menu mode="inline" items={itemsSider3} onClick={onMenuClick} />
        setSidermenu(menuconf)
      }
    },
    [siderUrl]   //依赖项为url地址的变化
  );

  //渲染
  return (
        <Layout>  {/* 采用Layout可以让Sider与Content横向排列 */}

          {/* 动态生成侧边menu */}  
          <Sider collapsible={false} collapsed={collapsed} trigger={null}>
            <Flex vertical justify="space-between" style={flexStyle}> {/* 目的是在侧边底添加一个button,用于收起导航 */}   
              {sidermenu}
              <Button icon={bticon} style={{borderRadius:"0",borderStyle:'none'}} onClick={onBtClick}>{btname}</Button>
            </Flex>
          </Sider>

          {/* 侧边menu在单击时输出内容在如下Content中 */}
          <Content style={{ margin: '0 16px'}}>  
            <Outlet />
          </Content>
          
        </Layout>
  )
}

export default SiderMenu;

侧边菜单click内容

p_content.js

import React from 'react';

const About = () => {
    return <>
        <h2>这是 about 页面</h2>
    </>
}

const Menu1 = () => {
    return <>
        这是 Menu1 页面<br/>
        这是 Menu1 页面<br/>
        这是 Menu1 页面<br/>
        这是 Menu1 页面
    </>
}

const Menu1_1 = () => {
    return <>
        这是 Menu1_1 页面
    </>
}

const Menu2 = () => {
    return <>
        这是 menu2 页面
    </>
}

const Menu2_1 = () => {
    return <>
        这是 menu2_1 页面
    </>
}

const Menu21 = () => {
    return <>
        这是 Menu21 页面
    </>
}

const Menu22 = () => {
    return <>
        这是 Menu22 页面
    </>
}

export {
    About,
    Menu1,
    Menu1_1,
    Menu2,
    Menu2_1,
    Menu21,
    Menu22,
}

login.js

login.js

import React from 'react';
import { useLocation } from 'react-router-dom';

const Login = () => {
    const location = useLocation()
    console.log(location)
    console.log(location.pathname )
    return <>
        这是 login 页面
    </>
}

export default Login;

路由配置

routers.js

import React from 'react';  
import {BrowserRouter,Routes,Route,Navigate} from 'react-router-dom'
import App from './test/test.tsx';
import {About,Menu1,Menu1_1,Menu2,Menu2_1,Menu21,Menu22} from './test/p_content.js';
import Login from './test/login.js';
import SiderMenu from './test/p_sider.tsx';

export default function Routers(){

    return(
        <BrowserRouter>
        <Routes>
          {/*路由条目。路由可以嵌套*/}
          <Route path='/' element={<Navigate to="/admin/about" />}  />
          <Route path='/login' element={<Login/>} />
          <Route path='/admin'  element={<App />} >
               {/* 子路由,url为:  / + 子的path */}
               <Route path='about' element={<About />} />
               <Route path='menu1' element={<Menu1 />} />
               <Route path='menu2' element={<Menu2 />} />
               <Route path='menu21' element={<Menu21 />} />
               <Route path='menu22' element={<Menu22 />} />
               <Route path='sider/:menu' element={<SiderMenu />} >
                    <Route path='menu1' element={<Menu1 />} />
                    <Route path='menu11' element={<Menu1_1 />} />
                    <Route path='menu2' element={<Menu2 />} />
                    <Route path='menu21' element={<Menu2_1 />} />
               </Route> 
          </Route>
          <Route path = '*' element={<p>未匹配到该路由请先设置路由页面 </p>} />
        </Routes>
       </BrowserRouter>
    )
}

入口文件

inex.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import { ConfigProvider,theme } from 'antd'
import Routers from './router.js';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <ConfigProvider
    locale={'zh_CN.UTF-8'}
    theme={{
      algorithm: theme.darkAlgorithm,  //默认有3个theme:defaultAlgorithm,darkAlgorithm, compactAlgorithm
      token: {
        colorPrimary: '#00a2fb',
        borderRadius: 0,
        colorBgContainer: 'rgba(7, 59, 108,.6)',
        //colorBgLayout:'red',
      },
      components: {
        Layout:{
          headerBg:'#03305a',
          siderBg:'#001529',
          footerBg:'#031322',
        }
      }
    }}
  >
    
    <Routers />
    
  </ConfigProvider>
);

logo.scss

.App-logo {
  width: 32px;
  height: 32px;
  margin: 15px;
  /*height: 40vmin;*/
  pointer-events: none;
}
/* 可以让图标转动起来 */
@media (prefers-reduced-motion: no-preference) {
  .App-logo {
    animation: App-logo-spin infinite 20s linear;
  }
}
/* 可以让图标转动起来 */
@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
React Antd是一个基于React开发的UI组件库,提供了一系列美观、易用的组件,能够帮助我们快速构建前端界面。而一个完整的后台项目框架首先需要具备以下几个方面的内容: 1. 路由管理:一个后台项目通常会有很多不同的页面,因此需要一个路由管理器来管理不同页面之间的切换。React Antd提供了React Router库来实现路由管理,我们可以通过配置路由信息来指定每个页面对应的路径,帮助我们实现页面间的跳转。 2. 权限管理:后台项目通常会有一些权限控制的需求,比如某些用户只能访问特定的页面或者执行特定的操作。React Antd提供了权限管理相关的组件,如Menu、Button等,我们可以根据不同的用户角色显示或隐藏某些功能。 3. 状态管理:一个完整的后台项目通常会有很多需要共享的状态,比如用户信息、页面数据等。React Antd本身并不提供状态管理解决方案,但可以与Redux、Mobx等状态管理工具配合使用,帮助我们管理全局的状态数据。 4. 表单验证:后台项目中经常需要进行表单验证,以确保用户输入的数据符合要求。React Antd提供了Form组件,可以方便地实现表单验证功能,如必填项校验、数据格式校验等。 5. 数据交互:后台项目通常需要与后端进行数据交互,如获取用户信息、提交表单数据等。React Antd并没有提供专门的数据交互解决方案,通常我们可以使用Axios等第三方库来实现数据的请求和响应。 综上所述,React Antd提供了一套美观、易用的UI组件,可以帮助我们快速构建后台项目。但在构建一个完整的后台项目框架时,我们还需要根据项目的具体需求,结合以上提到的几个方面来进行完善。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值