前两天有分享一篇electron27.x+react18 hooks搭建电脑端项目实践。
https://segmentfault.com/a/1190000044340077

今天再分享一篇electron+react18+arco-design搭建跨桌面端后台管理系统实例应用。

image.png

image.png

electron-react-admin支持亮色+黑夜主题。

开发技术

  • 开发工具:VScode
  • 框架技术:electron27+vite4+react18+zustand+react-router4
  • UI组件库:arco-design (字节react轻量级UI组件库)
  • 样式处理:sass^1.69.5
  • 图表组件:bizcharts^4.1.23
  • MD编辑器组件:@uiw/react-md-editor
  • 本地存储管理:zustand^4.4.4
  • 打包管理:electron-builder

image.png

项目结构图

34e22a38571e62f3ab5df6653c592eb6_1289798-20231030105539841-985724576.png

f1d8340e4151a2c09715017fd3fe6a7c_1289798-20231030115426988-254786437.png

image.png

image.png

image.png

1f50c9df90306bca1c6962f896f14eb1_1289798-20231030111401671-1062844939.png

d15d239c7ad87aaef449733f1a44f237_1289798-20231030111534818-1275887200.png

image.png

17895d8f8bfdb92bafa47b6f21fe7de2_1289798-20231030111931929-1633399800.png

image.png

electron-main.js主进程

/**
 * Electron主进程入口
 * @author Hs
 */

const { app, BrowserWindow } = require('electron')

const Windows = require('./src/windows')

// 忽略安全警告
// ectron Security Warning (Insecure Content-Security-Policy)
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'

const createWindow = () => {
    let win = new Windows()
    win.createWin({ isMainWin: true })
}

app.whenReady().then(() => {
    createWindow()
    app.on('activate', () => {
        if (BrowserWindow.getAllWindows().length === 0) createWindow()
    })
})

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') app.quit()
})

electron-preload.js预加载文件

/**
 * Electron预加载脚本
 * @author Hs  Q:282310962
 */

const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
    // 通过 channel 向主进程发送异步消息。主进程使用 ipcMain.on() 监听 channel
    send: (channel, data) => {
        ipcRenderer.send(channel, data)
    },

    // 通过 channel 向主进程发送消息,并异步等待结果。主进程应该使用 ipcMain.handle() 监听 channel
    invoke: (channel, data) => {
        return new Promise(resolve => ipcRenderer.invoke(channel, data).then(res => resolve(res)).catch(e => console.log(e)))
    },

    // 监听 channel 事件
    receive: (channel, func) => {
        console.log("preload-receive called. args: ")
        ipcRenderer.on(channel, (event, ...args) => func(event, ...args))
    },

    // 一次性监听事件
    once: (channel, func) => {
        ipcRenderer.once(channel, (event, ...args) => func(event, ...args))
    }
})

渲染进程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'

import { launchWin } from '@/windows/action'

launchWin().then(config => {
    console.log('——+——+——窗口参数:', config)
    console.log('——+——+——窗口id:', config.id)

    // 设置全局存储窗口配置
    window.config = config

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

image.png

App.jsx主模板

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 './router'

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

国际化+主题模式

image.png

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

zustand状态管理store/app.js配置

/**
 * react状态管理库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'
        }
    )
)

c21145bb0bc3a503fc2cacd686f17fbe_1289798-20231030114551348-1885126075.png

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] || {}
}

好了,以上就是electron27+react18开发客户端后台管理的一些知识分享。


xiaoyan2017
765 声望314 粉丝

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