React-Admin后台管理模板|react18+arco+zustand后台解决方案

  • React-Admin后台管理模板|react18+arco+zustand后台解决方案已关闭评论
  • 97 次浏览
  • A+
所属分类:Web前端
摘要

基于react18.x+vite4+arco-design 自研中后台管理系统解决方案ReactAdmin。 react-vite-admin 基于vite4搭建react18.x后台管理项目。使用了react18 hooks+arco.design+zustand+bizcharts 等技术实现权限管理模板框架。支持暗黑/亮色主题、i18n国际化、动态权限鉴定、3种布局模板、tab路由标签栏 等功能。

基于react18.x+vite4+arco-design自研中后台管理系统解决方案ReactAdmin

react-vite-admin 基于vite4搭建react18.x后台管理项目。使用了react18 hooks+arco.design+zustand+bizcharts等技术实现权限管理模板框架。支持暗黑/亮色主题、i18n国际化、动态权限鉴定、3种布局模板、tab路由标签栏等功能。

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React18Admin管理系统是首创自研的轻量级中后台框架,构建运行速度快,支持dark/light主题模式。

React-Admin后台管理模板|react18+arco+zustand后台解决方案

技术栈

  • 编辑器:Vscode
  • 框架技术:react18+vite4+react-router+zustand+axios
  • 组件库:arco-design (字节前端react组件库)
  • 路由管理:react-router-dom^6.16.0
  • 状态管理:zustand^4.4.1
  • 模拟数据:mockjs^1.1.0
  • 模拟请求:axios^1.5.1
  • 图表库:bizcharts^4.1.22
  • 编辑器组件:@wangeditor/editor-for-react^1.0.6
  • markdown编辑器:@uiw/react-md-editor^3.23.6
  • 请求进度插件:nprogress^0.2.0

React-Admin后台管理模板|react18+arco+zustand后台解决方案

react-admin采用字节出品的react桌面端组件库arco.design

React-Admin后台管理模板|react18+arco+zustand后台解决方案

特性

  1. 基于vite4.x构建react18后台,轻/快/小
  2. 使用最新前端技术栈react18、zustand、bizcharts、react-router、axios
  3. 搭配清新react组件库arco.design
  4. 支持中英文/繁体国际化语言
  5. 支持动态路由权限验证
  6. 支持动态tabs标签栏控制
  7. 内置多种模板布局样式

React-Admin后台管理模板|react18+arco+zustand后台解决方案

项目结构目录

采用标准化的react目录结构,整个项目使用react18 function语法编码开发。

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

构建预览图

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

wangeditor-react图文编辑器使用的是wangeditor的react版本,支持dark/light主题。

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

react-md-editor基于react的markdown编辑器,支持dark/light主题。

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

react18-scrollbar项目中使用的虚拟滚动条基于react18自定义组件实现功能。

React-Admin后台管理模板|react18+arco+zustand后台解决方案

// 引入滚动条组件 import RScroll from '@/components/rscroll'  <RScroll autohide maxHeight={100}>     包裹需要滚动的内容块。。。 </RScroll>

React18-Admin布局模板

React-Admin后台管理模板|react18+arco+zustand后台解决方案

如上图:支持分栏+垂直+水平3种通用布局模板。也可以定制化模板样式。

React-Admin后台管理模板|react18+arco+zustand后台解决方案

/**  * 主布局模板  * @author Hs Q:282310962 */  import { useMemo } from 'react' import { appStore } from '@/store/app'  import Columns from './template/columns' import Vertical from './template/vertical' import Transverse from './template/transverse'  function Layout() {     const { config: { skin, layout } } = appStore()      // 布局模板     const LayoutComponent = useMemo(() => {         switch(layout) {             case 'columns':                 return Columns             case 'vertical':                 return Vertical             case 'transverse':                 return Transverse             default:                 return Columns         }     }, [layout])          return (         <div className="radmin__container">             <LayoutComponent />         </div>     ) }  export default Layout

主模板Main.jsx动态Permission鉴权验证。

import './index.scss' import { Outlet } from 'react-router-dom' import RScroll from '@/components/rscroll' import Permission from '@/components/Permission' import Forbidden from '@/views/error/forbidden' import { useRoute } from '@/hooks/useRoutes'  export default function Main() {     const route = useRoute()      return (         <>             <RScroll>                 <div className="ra__layout-main__wrapper">                     {/* 鉴权组件 */}                     <Permission                         roles={route?.meta?.roles}                         content={<Forbidden />}                     >                         <Outlet />                     </Permission>                 </div>             </RScroll>         </>     ) }

react-router路由配置

React-Admin后台管理模板|react18+arco+zustand后台解决方案

/**  * @title    react-router-dom v6路由配置管理  * @author    andy */  import { useRoutes, Navigate } from 'react-router-dom'  import Error from '@views/error/404'  // 批量导入modules路由 const modules = import.meta.glob('./modules/*.jsx', { eager: true }) const patchRoutes = Object.keys(modules).map(key => modules[key].default).flat()  // useRoutes集中式路由配置 export const routes = [     {         path: '/',         element: <Navigate to="/home" replace={true} />,         meta: {             isWhite: true // 路由白名单         }     },     ...patchRoutes,     // 404模块 path="*"不能省略     {         path: '*',         element: <Error />,         meta: {             isWhite: true         }     } ]  const Router = () => useRoutes(routes)  export default Router

lazyload.jsx配置

/**  * 延迟加载提示 */  import { Suspense } from 'react' import { Spin } from '@arco-design/web-react' import NprogressLoading from './nprogress'  // 加载提示 const SpinLoading = () => {     return (         <Spin             tip='loading...'             style={{                 width: '100%'             }}         />     ) }  // 延迟加载 const lazyload = LazyComponent => {     // React 16.6 新增了<Suspense>组件,懒加载的模式需要我们给他加上一层 Loading的提示加载组件     // return <Suspense fallback={<SpinLoading />}><LazyComponent /></Suspense>     return <Suspense fallback={<NprogressLoading />}><LazyComponent /></Suspense> }  export default lazyload

NProgress.jsx配置

/**  * 加载进度条NProgress */  import { Component } from 'react' import NProgress from 'nprogress' import 'nprogress/nprogress.css'  export default class NprogressLoading extends Component {     constructor(props) {         super(props)         NProgress.set(.4)         NProgress.start()     }      componentDidMount() {         NProgress.done()     }      render() {         return <div />     } }

主路由main.jsx配置

/**  * 主路由配置  * @author Hs */  import { lazy } from 'react' import {     IconHome, IconDashboard, IconLink, IconCommand, IconUserGroup, IconLock,     IconMenu, IconSafe, IconBug, IconHighlight, IconUnorderedList, IconStop } from '@arco-design/web-react/icon' import Layout from '@/layouts' import Blank from '@/layouts/blank' import lazyload from '../lazyload'  export default [     /*首页模块*/     {         path: '/home',         key: '/home', // 用于Menu组件跳转路由地址         element: <Layout />,         meta: {             // icon: 've-icon-home', // 菜单图标             icon: <IconHome />,             name: 'layout__main-menu__home', // i18n国际化标题             title: '主页',             isAuth: true, // 需要鉴权             isHidden: false, // 是否隐藏菜单             isAffix: true // 固定tabview标签栏(不可关闭)         },         children: [             {                 key: '/home',                 index: true,                 element: lazyload(lazy(() => import('@views/home'))),                 meta: {                     // icon: 've-icon-home',                     icon: <IconHome />,                     name: 'layout__main-menu__home-index',                     title: '首页',                     isAuth: true                 }             },             // 工作台             {                 path: 'dashboard',                 key: '/home/dashboard',                 element: lazyload(lazy(() => import('@views/home/dashboard'))),                 meta: {                     // icon: 've-icon-computer',                     icon: <IconDashboard />,                     name: 'layout__main-menu__home-workplace',                     title: '工作台',                     isAuth: true                 }             },             // 外部链接             {                 path: 'https://react.dev/',                 key: 'https://react.dev/',                 meta: {                     // icon: 've-icon-clip',                     icon: <IconLink />,                     name: 'layout__main-menu__home-apidocs',                     title: 'react.js官方文档',                     rootRoute: '/home'                 }             }         ]     },      /*组件模块*/     {         ...     },      /*用户管理模块*/     {         ...     },      /*权限模块*/     {         ...     },      /*错误模块*/     {         ...     } ]

备注:路由菜单参数配置说明。

/**  * @description 路由参数说明  * @param path ==> 路由地址标识  * @param key ==> 用于Menu组件跳转路由地址  * @param redirect ==> 重定向地址  * @param element ==> 视图页面路径  * 菜单信息(meta)  *         @param meta.icon ==> 菜单图标  *         @param meta.title ==> 菜单标题  *         @param meta.name ==> i18n国际化标题  *         @param meta.roles ==> 页面权限 ['admin', 'dev', 'test']  *         @param meta.isAuth ==> 是否需要验证  *         @param meta.isHidden ==> 是否隐藏页面  *         @param meta.isAffix ==> 是否固定标签(tabs标签栏不能关闭)  * */

react自定义路由菜单Menu

基于arco.design组件库提供的Menu组件封装三种不同的路由菜单。

React-Admin后台管理模板|react18+arco+zustand后台解决方案  React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

<RouteMenu />  <RouteMenu rootRouteEnable />  <RouteMenu rootRouteEnable mode="horizontal" />

RouteMenu路由菜单模板

/**  * 路由菜单模板 */  import './index.scss' import { useState, useMemo, useEffect } from 'react' import { useNavigate, useLocation } from 'react-router-dom' import { Menu } from '@arco-design/web-react' import Icon from '@components/Icon' import RouteSubMenu from './submenu' import { routes } from '@/routers' import { getCurrentRootRoute, findParentRoute } from '@/hooks/useRoutes' import Locales from '@/locales'  export default function RouteMenu(props) {     const {         // 菜单类型(垂直vertical 水平菜单horizontal 弹出pop)         mode = 'vertical',         // 菜单风格('light' | 'dark')         theme = 'light',         // 是否开启一级路由菜单         rootRouteEnable = false,          style = {}     } = props      const navigate = useNavigate()     const { pathname } = useLocation()     const t = Locales()     const [openKeys, setOpenKeys] = useState([])     const rootRoute = getCurrentRootRoute()     const filterRoutes = routes.filter(item => !item?.meta?.isWhite)      const menuRoutes = useMemo(() => {         if(rootRouteEnable) {             return filterRoutes         }         // 过滤一级菜单         return filterRoutes.find(item => item.path == rootRoute && item.children)?.children     }, [pathname])      useEffect(() => {         setOpenKeys(getKeys(pathname))     }, [pathname])      // 获取选中菜单路由keys数组     const getKeys = (key) => {         return findParentRoute(menuRoutes, key)?.map(item => item?.key)     }      const handleNavigate = (key) => {         const reg = /[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+.?/         if(reg.test(key)) {             window.open(key)         }else {             navigate(key)         }     }      return (         <Menu             className="ra__menus"             mode={mode}             theme={theme}             selectedKeys={[pathname]}             openKeys={openKeys}             levelIndent={28}             style={{ ...style }}             onClickMenuItem={handleNavigate}             onClickSubMenu={(_, openKeys) => {                 setOpenKeys(openKeys)             }}         >             { menuRoutes.map(item => {                 if(item?.children) {                     return RouteSubMenu(item, t)                 }                 return (                     !item?.meta?.isHidden &&                     <Menu.Item className="ra__menuItem" key={item.redirect || item.key}>                         { item?.meta?.icon && <Icon name={item.meta.icon} size={18} style={{marginRight: 10}} /> }                         { item?.meta?.name && <span>{t[item.meta.name]}</span> }                     </Menu.Item>                 )             })}         </Menu>     ) }

react18状态管理zustand

React-Admin后台管理模板|react18+arco+zustand后台解决方案

Zustand新一代react状态管理工具,内置多种插件,支持persist本地存储服务。

/**  * react18状态管理库Zustand4,中间件persist本地持久化存储 */ import { create } from 'zustand' import { persist, createJSONStorage } from 'zustand/middleware' import { generate, getRgbStr } from '@arco-design/color'  export const appStore = create(     persist(         (set, get) => ({             // 语言(中文zh-CN 英文en 繁体字zh-TW)             lang: 'zh-CN',             // 角色类型 roles: ['admin'] / roles: ['admin', 'dev'] / roles: ['dev', test']             roles: ["dev"],             // 配置信息             config: {                 // 布局(分栏columns 纵向vertical 横向transverse)                 layout: 'columns',                 // 模式(亮色light - 暗黑dark)                 mode: 'light',                 // 主题色                 theme: '#3491FA',                 // 是否折叠菜单                 collapsed: false,                 // 开启面包屑导航                 breadcrumb: true,                 // 开启标签栏                 tabsview: true,                 tabRoutes: [],                 // 显示搜索                 showSearch: true,                 // 显示全屏                 showFullscreen: true,                 // 显示语言                 showLang: true,                 // 显示公告                 showNotice: true,                 // 显示底部                 showFooter: false             },                          // 更新配置             updateConfig: (key, value) => set({                 config: { ...get().config, [key]: value }             }),             // 设置角色             setRoles: (roles) => set({roles}),             // 设置多语言             setLang: (lang) => set({lang}),             // 设置主题模式             setMode: (mode) => {                 if(mode == 'dark') {                     // 设置为暗黑主题                     document.body.setAttribute('arco-theme', 'dark')                 }else {                     // 恢复亮色主题                     document.body.removeAttribute('arco-theme')                 }                 get().updateConfig('mode', mode)             },             // 设置主题样式             setTheme: (theme) => {                 const colors = generate(theme, { list: true })                 colors.map((item, index) => {                     const rgbStr = getRgbStr(item)                     document.body.style.setProperty(`--arcoblue-${index + 1}`, rgbStr)                 })                 get().updateConfig('theme', theme)             }         }),         {             name: 'appState',             // name: 'app-store', // name of the item in the storage (must be unique)             // storage: createJSONStorage(() => sessionStorage), // by default, 'localStorage'         }     ) )

react18国际化配置i18n

React-Admin后台管理模板|react18+arco+zustand后台解决方案

React-Admin后台管理模板|react18+arco+zustand后台解决方案

/**  * 国际化配置  * @author YXY  */  import { appStore } from '@/store/app'  // 引入语言配置 import enUS from './en-US' import zhCN from './zh-CN' import zhTW from './zh-TW'  export const locales = {     'en': enUS,     'zh-CN': zhCN,     'zh-TW': zhTW }  export default (locale) => {     const appState = appStore()     const lang = appState.lang || 'zh-CN'      return (locale || locales)[lang] || {} }

App.jsx引入arco.design组件库语言包。

/**  * 入口模板  * @author Hs */  import { useEffect, useMemo } from 'react' import { HashRouter } from 'react-router-dom' // 通过 ConfigProvider 组件实现国际化 import { ConfigProvider } from '@arco-design/web-react' // 引入语言包 import enUS from '@arco-design/web-react/es/locale/en-US' import zhCN from '@arco-design/web-react/es/locale/zh-CN' import zhTW from '@arco-design/web-react/es/locale/zh-TW'  import { AuthRouter } from '@/hooks/useRoutes' import { appStore } from '@/store/app'  // 引入路由配置 import Router from './routers'  function App() {     const { lang, config: { mode, theme }, setMode, setTheme } = appStore()      const locale = useMemo(() => {         switch(lang) {             case 'en':                 return enUS             case 'zh-CN':                 return zhCN             case 'zh-TW':                 return zhTW             default:                 return zhCN         }     }, [lang])      useEffect(() => {         setMode(mode)         setTheme(theme)     }, [])      return (         <ConfigProvider locale={locale}>             <HashRouter>                 <AuthRouter>                     <Router />                 </AuthRouter>             </HashRouter>         </ConfigProvider>     ) }  export default App

Lang.jsx配置

import { Dropdown, Menu, Button } from '@arco-design/web-react' import Icon from '@components/Icon' import { appStore } from '@/store/app'  export default function Lang() {     const { lang, setLang } = appStore()      const handleLang = val => {         setLang(val)     }      return (         <Dropdown             position="bottom"             droplist={                 <Menu className="radmin__dropdownLang" defaultSelectedKeys={[lang]} onClickMenuItem={handleLang}>                     <Menu.Item key='zh-CN'>简体中文 <span>zh-CN</span></Menu.Item>                     <Menu.Item key="zh-TW">繁体字 <span>zh-TW</span></Menu.Item>                     <Menu.Item key="en">英文 <span>en</span></Menu.Item>                 </Menu>             }         >             <Button                 shape="circle"                 size="small"                 icon={<Icon name="ve-icon-lang" />}             />         </Dropdown>     ) }

Tabs.jsx动态路由栏

React-Admin后台管理模板|react18+arco+zustand后台解决方案

项目中动态路由栏tabs采用arco.design组件库Tabs组件自定义实现功能。

<Tabs     activeTab={pathname}     editable     showAddButton={false}     onDeleteTab={key => delTabs(key)} >     { tabRoutes.map(item => (         <Tabs.TabPane             closable={!item?.meta?.isAffix}             key={item?.redirect || item?.key}             title={                 <Dropdown                     trigger='contextMenu'                     position='bl'                     droplist={                         <Menu className="ra__dropdownContext" onClickMenuItem={(key, e) => handleClickMenuItem(key, e, item)}>                             <Menu.Item key="close" disabled={item?.meta?.isAffix}><Icon name="ve-icon-close" />{t['tabview__contextmenu-close']}</Menu.Item>                             <Menu.Item key="closeLeft" disabled={isFirstTab()}><Icon name="ve-icon-prev" />{t['tabview__contextmenu-closeleft']}</Menu.Item>                             <Menu.Item key="closeRight" disabled={isLastTab()}><Icon name="ve-icon-next" />{t['tabview__contextmenu-closeright']}</Menu.Item>                             <Menu.Item key="closeOther"><Icon name="ve-icon-reset" />{t['tabview__contextmenu-closeother']}</Menu.Item>                             <Menu.Item key="closeAll"><Icon name="ve-icon-close-circle-o" />{t['tabview__contextmenu-closeall']}</Menu.Item>                         </Menu>                     }                     onVisibleChange={visible=>handleOpenContextMenu(visible, item)}                 >                     <span className="ra__tabsview-title" onClick={() => navigate(item?.redirect || item?.key)}>                         <TabIcon path={item?.key} />                         { t[item?.meta?.name] }                     </span>                 </Dropdown>             }         />     ))} </Tabs>

export default function TabsView() {     const { pathname } = useLocation()     const navigate = useNavigate()     const [selectedTab, setSelectedTab] = useState()     const { config: { tabRoutes }, updateConfig } = appStore()     const route = useRoute()     const t = Locales()      useEffect(() => {         addTabs()     }, [pathname])      // 添加     const addTabs = () => {         const tabIndex = tabRoutes.findIndex(item => item?.key === pathname)         let newTabs = tabRoutes         if(tabIndex == -1) {             newTabs.push(route)         }         newTabs.map(item => {             item.isActive = false             if(item?.key === pathname) {                 item.isActive = true             }         })         updateConfig('tabRoutes', newTabs)     }      // 删除     const delTabs = (path) => {         const tabIndex = tabRoutes.findIndex(item => item?.key === path)         let newTabs = tabRoutes         if(tabIndex > -1) {             newTabs.splice(tabIndex, 1)             updateConfig('tabRoutes', newTabs)             updateTabs(newTabs)         }     }      // 删除左侧标签     const delLeftTabs = (path) => {         const tabIndex = tabRoutes.findIndex(item => item?.key === path)         let newTabs = tabRoutes         if(tabIndex > -1) {             newTabs = newTabs.filter((item, i) => item?.meta?.isAffix || i >= tabIndex)             updateConfig('tabRoutes', newTabs)             updateTabs(newTabs)         }     }      // 删除右侧标签     const delRightTabs = (path) => {         const tabIndex = tabRoutes.findIndex(item => item?.key === path)         let newTabs = tabRoutes         if(tabIndex > -1) {             newTabs = newTabs.filter((item, i) => item?.meta?.isAffix || i <= tabIndex)             updateConfig('tabRoutes', newTabs)             updateTabs(newTabs)         }     }      // 删除其它     const delOtherTabs = (path) => {         let newTabs = tabRoutes.filter(item => item?.meta?.isAffix || item?.key === path)         updateConfig('tabRoutes', newTabs)         updateTabs(newTabs)     }      // 删除所有     const delAllTabs = () => {         let newTabs = tabRoutes.filter(item => item?.meta?.isAffix)         updateConfig('tabRoutes', newTabs)         updateTabs(newTabs)     }      // 更新跳转选项卡     const updateTabs = (tabs) => {         const nextTab = tabs[tabs.length + 1] || tabs[tabs.length - 1]         if(!nextTab) return         navigate(nextTab?.redirect || nextTab?.key)     }      // 是否第一个标签     const isFirstTab = () => {         return selectedTab?.key === tabRoutes[0]?.key || selectedTab?.key === '/home'     }     // 是否最后一个标签     const isLastTab = () => {         return selectedTab?.key === tabRoutes[tabRoutes.length - 1]?.key     }      // 打开右键菜单     const handleOpenContextMenu = (visible, item) => {         if(visible) {             setSelectedTab(item)         }     }      // 点击右键菜单项     const handleClickMenuItem = (key, e, item) => {         e.stopPropagation()         const path = item?.key         switch(key) {             case 'close':                 delTabs(path)                 break             case 'closeLeft':                 delLeftTabs(path)                 break             case 'closeRight':                 delRightTabs(path)                 break             case 'closeOther':                 delOtherTabs(path)                 break             case 'closeAll':                 delAllTabs()                 break         }     }      return (         ...     ) }

OK,以上就是react18+arco开发后台系统的一些分享,希望对大家有所帮助~ ?

最后附上两个最新项目实例

https://www.cnblogs.com/xiaoyan2017/p/17630296.html

https://www.cnblogs.com/xiaoyan2017/p/17468074.html

React-Admin后台管理模板|react18+arco+zustand后台解决方案