1
头图

项目介绍

随着vite.js生态逐步成熟,越来越多的开发者倾向于使用vite构建工具创建react项目。今天给大家分享react18+arco-design开发清新后台管理系统ReactAdmin

React-Vite-Admin 使用前端技术react18+arco+zustand+bizcharts搭建后台管理框架。

使用技术

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

react-admin项目结构

整个项目采用vite4构建,使用react18 function编码方式开发。

react-vite-admin 支持暗黑/亮色主题模式、i18n国际化配置、动态路由权限鉴定、3种布局模板、tab路由标签栏等功能。

main.jsx入口配置

/**
 * 入口配置文件
 * @author Hs
*/

import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import '@arco-design/web-react/dist/css/arco.css'
import './styles/common.scss'

ReactDOM.createRoot(document.getElementById('root')).render(<App />)

App.jsx入口模板

引入公共模板、配置全局语言包及路由鉴权功能。

/**
 * 入口模板
 * @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

react-admin布局模板

整个项目内置了3种布局样式(分栏/垂直布局/水平布局)。
adafc1f599f4dba8b2e21682b507f028_1289798-20231017084804366-1526972436.png

布局模板在layouts目录下的template文件夹。

image.png

/**
 * Layout布局模板
 * @author YXY
*/

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

react-router v6路由配置

cfc9f9f5567cbd91c37e922ac553d9d1_1289798-20231017090936959-1596601401.png

/**
 * @title    react-router-dom v6路由配置管理
 * @author   YXY  Q:282310962
*/

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

modules/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路由菜单

image.png

如上图:使用arco组件库Menu组件封装了三种不同路由菜单。

/**
 * 路由菜单模板
*/

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>
    )
}

react状态管理

react-admin使用了全新的新一代轻盈状态管理库Zustand4。内置了多种中间件插件功能。

d757fb825b476c9d5dbcf752c836cd65_1289798-20231017093057983-1460989159.png

/**
 * 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'
        }
    )
)

react国际化配置

6032fbd37e0c56352d70153af98f005d_1289798-20231017093750196-2139331344.png

react-admin支持中英文/繁体三种语言。

0a4199faf07e9047658fce8442f1ceb6_1289798-20231017093850844-390073734.png

/**
 * 国际化配置
 * @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] || {}
}
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>
    )
}

综上就是react18+arco.design+zustand开发后台管理系统的一些分享知识。

https://segmentfault.com/a/1190000043942666

https://segmentfault.com/a/1190000044116713


xiaoyan2017
765 声望318 粉丝

web前端开发爱好者,专注于前端h5、jquery、vue、react、angular等技术研发实战项目案例。