Electron-React18-MacOS桌面管理系统|electron27+react仿mac桌面
基于React18+Electron27+ArcoDesign仿macOS桌面端系统框架ElectronMacOS。
electron-react-macOs 基于electron27.x+vite4+react18+arcoDesign+zustand等技术构建桌面版仿MacOs框架系统解决方案。支持中英文/繁体、dark+light主题、桌面多层级路由、多窗口路由页面、动态换肤、Dock悬浮菜单等功能。

ElectronReactOS系统是首创自研的桌面多层级路由菜单、支持electron多开窗口+弹窗路由窗口。

技术栈
- 开发工具:vscode
- 框架技术:vite4+react18+zustand+react-router
- 跨端技术:electron^27.0.1
- 打包工具:electron-builder^24.6.4
- UI组件库:arco-design (字节react轻量级UI组件库)
- 图表组件:bizcharts^4.1.23
- 拖拽库:sortablejs
- 模拟请求:axios
- 弹窗组件:rdialog (基于react多功能layer弹窗)
- 美化滚动条:rscroll (基于react虚拟滚动条组件)


特性
- 桌面路由页面支持暗黑+亮色模式
- 内置中英文/繁体国际化
- 经典桌面Dock悬浮菜单
- 可拖拽桌面路由+程序坞Dock菜单
- 桌面路由支持多个子级路由配置
- 动态视觉效果,自定义桌面换肤背景
- 可视化多窗口路由,支持electron新开窗口+rdialog弹窗页面

大家如果对Electron封装多窗口感兴趣,可以去看看下面这篇分享文章。
https://www.cnblogs.com/xiaoyan2017/p/17788495.html
项目结构
使用vite4构建react18项目,整合electron跨端技术,搭建桌面版OS管理系统。



















Electron桌面os布局模板
桌面os布局分为顶部操作栏+桌面端路由菜单+底部Dock菜单三大模块。

<div className="radmin__layout flexbox flex-col">
{/* 导航栏 */}
<Header /> {/* 桌面区域 */}
<div className="ra__layout-desktop flex1 flexbox" onContextMenu={handleDeskCtxMenu} style={{marginBottom: 70}}>
<DeskMenu />
</div> {/* Dock菜单 */}
<Dock />
</div>
Electron+React实现Dock菜单



底部dock菜单采用背景滤镜模糊效果、支持自适应伸缩、拖拽排序等功能。
<div className="ra__docktool">
<div className={clsx('ra__dock-wrap', !dock ? 'compact' : 'split')}>
{dockMenu.map((res, key) => {
return (
<div key={key} className="ra__dock-group">
{ res?.children?.map((item, index) => {
return (
<a key={index} className={clsx('ra__dock-item', {'active': item.active, 'filter': item.filter})} onClick={() => handleDockClick(item)}>
<span className="tooltips">{item.label}</span>
<div className="img">
{ item.type != 'icon' ? <img src={item.image} /> : <Icon name={item.image} size={32} style={{color: 'inherit'}} /> }
</div>
</a>
)
})}
</div>
)
})}
</div>
</div>
const dockMenu = [
{
// 图片图标
children: [
{label: 'Safari', image: '/static/mac/safari.png', active: true},
{label: 'Launchpad', image: '/static/mac/launchpad.png'},
{label: 'Contacts', image: '/static/mac/contacts.png'},
{label: 'Messages', image: '/static/mac/messages.png', active: true}
]
},
{
// 自定义iconfont图标
children: [
{label: 'Home', image: <IconDesktop />, type: 'icon'},
{label: 'About', image: 've-icon-about', type: 'icon'}
]
},
{
children: [
{label: 'Appstore', image: '/static/mac/appstore.png'},
{label: 'Mail', image: '/static/mac/mail.png'},
{label: 'Maps', image: '/static/mac/maps.png', active: true},
{label: 'Photos', image: '/static/mac/photos.png'},
{label: 'Facetime', image: '/static/mac/facetime.png'},
{label: 'Calendar', image: '/static/mac/calendar.png'},
{label: 'Notes', image: '/static/mac/notes.png'},
{label: 'Calculator', image: '/static/mac/calculator.png'},
{label: 'Music', image: '/static/mac/music.png'}
]
},
{
children: [
{label: 'System', image: '/static/mac/system.png', active: true, filter: true},
{label: 'Empty', image: '/static/mac/bin.png', filter: true}
]
}
] // 点击dock菜单
const handleDockClick = (item) => {
const { label } = item
if(label == 'Home') {
createWin({
title: '首页',
route: '/home',
width: 900,
height: 600
})
}else if(label == 'About') {
setWinData({ type: 'CREATE_WIN_ABOUT' })
}else if(label == 'System') {
createWin({
title: '网站设置',
route: '/setting/system/website',
isNewWin: true,
width: 900,
height: 600
})
}
} useEffect(() => {
const dockGroup = document.getElementsByClassName('ra__dock-group')
// 组拖拽
for(let i = 0, len = dockGroup.length; i < len; i++) {
Sortable.create(dockGroup[i], {
group: 'share',
handle: '.ra__dock-item',
filter: '.filter',
animation: 200,
delay: 0,
onEnd({ newIndex, oldIndex }) {
console.log('新索引:', newIndex)
console.log('旧索引:', oldIndex)
}
})
}
}, [])
Electron+React桌面多级路由菜单

如上图:桌面菜单配置支持多级路由。
import { lazy } from 'react'
import {
IconDesktop, IconDashboard, IconLink, IconCommand, IconUserGroup, IconLock,
IconSafe, IconBug, IconUnorderedList, IconStop
} from '@arco-design/web-react/icon'
import Layout from '@/layouts'
import Desk from '@/layouts/desk'
import Blank from '@/layouts/blank'
import lazyload from '../lazyload'
export default [
/* 桌面模块 */
{
path: '/desk',
key: '/desk',
element: <Desk />,
meta: {
icon: <IconDesktop />,
name: 'layout__main-menu__desk',
title: 'Appstore',
isWhite: true, // 路由白名单
isAuth: true, // 需要鉴权
isHidden: false, // 是否隐藏菜单
}
},
{
path: '/home',
key: '/home',
element: <Layout>{lazyload(lazy(() => import('@views/home')))}</Layout>,
meta: {
icon: '/static/mac/appstore.png',
name: 'layout__main-menu__home-index',
title: '首页',
isAuth: true,
isNewWin: true
}
},
{
path: '/dashboard',
key: '/dashboard',
element: <Layout>{lazyload(lazy(() => import('@views/home/dashboard')))}</Layout>,
meta: {
icon: <IconDashboard />,
name: 'layout__main-menu__home-workplace',
title: '工作台',
isAuth: true
}
},
{
path: 'https://react.dev/',
key: 'https://react.dev/',
meta: {
icon: <IconLink />,
name: 'layout__main-menu__home-apidocs',
title: 'react.js官方文档',
rootRoute: '/home'
}
},
/* 组件模块 */
{
path: '/components',
key: '/components',
redirect: '/components/table/allTable', // 一级路由重定向
element: <Blank />,
meta: {
icon: <IconCommand />,
name: 'layout__main-menu__component',
title: '组件示例',
isAuth: true,
isHidden: false
},
children: [
{
path: 'table',
key: '/components/table',
element: <Blank />,
meta: {
icon: 've-icon-table',
name: 'layout__main-menu__component-table',
title: '表格',
isAuth: true
},
children: [
{
path: 'allTable',
key: '/components/table/allTable',
element: <Layout>{lazyload(lazy(() => import('@views/components/table/all')))}</Layout>,
meta: {
name: 'layout__main-menu__component-table_all',
title: '所有表格'
}
},
{
path: 'customTable',
key: '/components/table/customTable',
element: <Layout>{lazyload(lazy(() => import('@views/components/table/custom')))}</Layout>,
meta: {
name: 'layout__main-menu__component-table_custom',
title: '自定义表格'
}
},
{
path: 'search',
key: '/components/table/search',
element: <Blank />,
meta: {
name: 'layout__main-menu__component-table_search',
title: '搜索'
},
children: [
{
path: 'searchList',
key: '/components/table/search/searchList',
element: <Layout>{lazyload(lazy(() => import('@views/components/table/search')))}</Layout>,
meta: {
name: 'layout__main-menu__component-table_search_list',
title: '搜索列表'
}
}
]
}
]
},
{
path: 'list',
key: '/components/list',
element: <Layout>{lazyload(lazy(() => import('@views/components/list')))}</Layout>,
meta: {
icon: 've-icon-order-o',
name: 'layout__main-menu__component-list',
title: '列表'
}
},
{
path: 'form',
key: '/components/form',
element: <Blank />,
meta: {
icon: 've-icon-exception',
name: 'layout__main-menu__component-form',
title: '表单',
isAuth: true
},
children: [
{
path: 'allForm',
key: '/components/form/allForm',
element: <Layout>{lazyload(lazy(() => import('@views/components/form/all')))}</Layout>,
meta: {
name: 'layout__main-menu__component-form_all',
title: '所有表单'
}
},
{
path: 'customForm',
key: '/components/form/customForm',
element: <Layout>{lazyload(lazy(() => import('@views/components/form/custom')))}</Layout>,
meta: {
name: 'layout__main-menu__component-form_custom',
title: '自定义表单'
}
}
]
},
{
path: 'markdown',
key: '/components/markdown',
element: <Layout>{lazyload(lazy(() => import('@views/components/markdown')))}</Layout>,
meta: {
icon: <IconUnorderedList />,
name: 'layout__main-menu__component-markdown',
title: 'markdown编辑器'
}
},
{
path: 'qrcode',
key: '/components/qrcode',
meta: {
icon: 've-icon-qrcode',
name: 'layout__main-menu__component-qrcode',
title: '二维码'
}
},
{
path: 'print',
key: '/components/print',
meta: {
icon: 've-icon-printer',
name: 'layout__main-menu__component-print',
title: '打印'
}
},
{
path: 'pdf',
key: '/components/pdf',
meta: {
icon: 've-icon-pdffile',
name: 'layout__main-menu__component-pdf',
title: 'pdf'
}
}
]
},
/* 用户管理模块 */
{
path: '/user',
key: '/user',
redirect: '/user/userManage',
element: <Blank />,
meta: {
// icon: 've-icon-team',
icon: <IconUserGroup />,
name: 'layout__main-menu__user',
title: '用户管理',
isAuth: true,
isHidden: false
},
children: [
...
]
},
/* 配置模块 */
{
path: '/setting',
key: '/setting',
redirect: '/setting/system/website',
element: <Blank />,
meta: {
icon: 've-icon-settings-o',
name: 'layout__main-menu__setting',
title: '设置',
isHidden: false
},
children: [
...
]
},
/* 权限模块 */
{
path: '/permission',
key: '/permission',
redirect: '/permission/admin',
element: <Blank />,
meta: {
// icon: 've-icon-unlock',
icon: <IconLock />,
name: 'layout__main-menu__permission',
title: '权限管理',
isAuth: true,
isHidden: false
},
children: [
...
]
}
]
DeskMenu.jsx模板
/**
* Desk桌面多层级路由菜单
* Q:282310962
*/ export default function DeskMenu() {
const t = Locales()
const filterRoutes = routes.filter(item => !item?.meta?.isWhite) // 桌面二级菜单弹框
const DeskPopup = (item) => {
const { key, meta, children } = item return (
!meta?.isHidden &&
<RScroll maxHeight={220}>
<div className="ra__deskmenu-popup__body">
{ children.map(item => {
if(item?.children) {
return DeskSubMenu(item)
}
return DeskMenu(item)
})}
</div>
</RScroll>
)
} // 桌面菜单项
const DeskMenu = (item) => {
const { key, meta, children } = item return (
!meta?.isHidden &&
<div key={key} className="ra__deskmenu-block">
<a className="ra__deskmenu-item" onClick={()=>handleDeskClick(item)} onContextMenu={handleDeskCtxMenu}>
<div className="img">
{meta?.icon ?
isImg(meta?.icon) ? <img src={meta.icon} /> : <Icon name={meta.icon} size={40} />
:
<Icon name="ve-icon-file" size={40} />
}
</div>
{ meta?.name && <span className="title clamp2">{t[meta.name]}</span> }
</a>
</div>
)
}
// 桌面二级菜单项
const DeskSubMenu = (item) => {
const { key, meta, children } = item return (
!meta?.isHidden &&
<div key={key} className="ra__deskmenu-block">
<a className="ra__deskmenu-item group" onContextMenu={e=>e.stopPropagation()}>
<Popover
title={<div className="ra__deskmenu-popup__title">{meta?.name && t[meta.name]}</div>}
content={() => DeskPopup(item)}
trigger="hover"
position="right"
triggerProps={{
popupStyle: {padding: 5},
popupAlign: {
right: [10, 45]
},
mouseEnterDelay: 300,
// showArrow: false
}}
style={{zIndex: 100}}
>
<div className="img">
{children.map((child, index) => {
if(child?.meta?.isHidden) return
return child?.meta?.icon ?
isImg(child?.meta?.icon) ? <img key={index} src={child.meta.icon} /> : <Icon key={index} name={child.meta.icon} size={10} />
:
<Icon key={index} name="ve-icon-file" size={10} />
})}
</div>
</Popover>
{ meta?.name && <span className="title clamp2">{t[meta.name]}</span> }
</a>
</div>
)
} // 点击dock菜单
const handleDeskClick = (item) => {
const { key, meta, element } = item 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 {
if(meta?.isNewWin) {
// 新窗口打开
createWin({
title: t[meta?.name] || meta?.title,
route: key,
width: 900,
height: 600
})
}else {
// 弹窗打开
rdialog({
title: t[meta?.name] || meta?.title,
content: <BrowserRouter>{element}</BrowserRouter>,
maxmin: true,
showConfirm: false,
area: ['900px', '550px'],
className: 'rc__dialogOS',
customStyle: {padding: 0},
zIndex: 100
})
}
}
} // 右键菜单
const handleDeskCtxMenu = (e) => {
e.stopPropagation()
let pos = [e.clientX, e.clientY]
rdialog({
type: 'contextmenu',
follow: pos,
opacity: .1,
dialogStyle: {borderRadius: 3, overflow: 'hidden'},
btns: [
{text: '打开'},
{text: '重命名/配置'},
{
text: '删除',
click: () => {
rdialog.close()
}
}
]
})
} useEffect(() => {
const deskEl = document.getElementById('deskSortable')
Sortable.create(deskEl, {
handle: '.ra__deskmenu-block',
animation: 200,
delay: 0,
onEnd({ newIndex, oldIndex }) {
console.log('新索引:', newIndex)
console.log('旧索引:', oldIndex)
}
})
}, []) return (
<div className="ra__deskmenu" id="deskSortable">
{ filterRoutes.map(item => {
if(item?.children) {
return DeskSubMenu(item)
}
return DeskMenu(item)
})}
</div>
)
}

OK,以上就是Electron27+React18开发仿制MacOS桌面系统的一些分享,希望对大家有些帮助哈~~
最后附上两个最近实例项目
https://www.cnblogs.com/xiaoyan2017/p/17695193.html
https://www.cnblogs.com/xiaoyan2017/p/17552562.html

Electron-React18-MacOS桌面管理系统|electron27+react仿mac桌面的更多相关文章
- Electron-Vite2-MacUI桌面管理框架|electron13+vue3.x仿mac桌面UI
基于vue3.0.11+electron13仿制macOS桌面UI管理系统ElectronVue3MacUI. 前段时间有分享一个vue3结合electron12开发后台管理系统项目.今天要分享的是最 ...
- electron之Windows下使用 html js css 开发桌面应用程序
1.atom/electron github: https://github.com/atom/electron 中文文档: https://github.com/atom/electron/tree ...
- 使用 Flutter 开发 Mac 桌面应用
Flutter 可以开发 Mac,Linux,Windows 桌面,但是对于平台目前只能打对于的包,以及调试本平台的包. 切换到 master 分支 首先必须切换到 master 分支.我之前在 de ...
- 烂泥:学习ubuntu远程桌面(二):远程桌面会话管理
本文由秀依林枫提供友情赞助,首发于烂泥行天下 在上一篇文章中,我们讲解了如何给ubuntu安装远程桌面及其配置,这篇文章我们再来讲解下有关ubuntu远程桌面会话的问题. 一.问题描述 在我们使用ub ...
- Citrix 服务器虚拟化之十八 桌面虚拟化之部署MCS随机桌面池
Citrix 服务器虚拟化之十八 桌面虚拟化之部署MCS随机桌面池 完成桌面模版的制作后,可以开始虚拟桌面池的发布 说明: 环境基于实验十七 1.登录DC服务器创建一个组织单位名为Citrix,然后 ...
- WIN7虚拟桌面创建(多屏幕多桌面)
Windows7/WIN7虚拟桌面怎么用怎么创建多桌面(摘录) 在使用电脑中经常会遇到桌面软件太多了不够用的感慨,那么要是一台电脑有多个桌面就好了.在windows10中自带已经支持了虚拟桌面,在wi ...
- win10 常用设置 桌面出来计算机图标,固定桌面摆好的图标设置方法,电脑设备ID方法
win10 常用设置 桌面出来计算机图标,固定桌面摆好的图标设置方法 桌面右键-->显示设置-->桌面图标设置 电脑设备ID:xxx查看方法:桌面右键-->显示设置-->关于
- mac远程桌面Microsoft Remote Desktop for Mac的安装与使用
mac远程桌面Microsoft Remote Desktop for Mac的安装与使用 学习了:https://blog.csdn.net/ytangdigl/article/details/78 ...
- 【桌面篇】Archlinux安装kde桌面
ArchLinux安装配置手册[桌面篇] 现在你的U盘可以拔掉了,重启后会发现和刚刚没什么区别,还是命令行的界面,别着急现在就带你安装桌面环境. 连接网络 首先检查一下网络是否连接成功 ping ww ...
- WPF将窗口置于桌面下方(可用于动态桌面)
WPF将窗口置于桌面下方(可用于动态桌面) 先来看一下效果: 界面元素很简单,就一个Button按钮,然后写个定时器,定时更新Button按钮中的内容为当前时间,下面来介绍下原理,和界面组成. 窗口介 ...
随机推荐
- 注意!JAVA中的值传递
前言:今天在解决一个问题时,程序总是不能输出正确值,分析逻辑思路没问题后,发现原来是由于函数传递导致了这个情况. LeetCode 113 问题:给你二叉树的根节点root和一个整数目标和target ...
- [kvm]cpu内存硬盘配置
修改CPU配置 如果配置了最大CPU # 临时 virsh setvcpus test 2 # 永久 virsh setvcpus test 2 --config 热增加虚拟机的CPU数后,使用lsc ...
- nlp入门(二) :商品信息可视化与文本分析实战
源码请到:自然语言处理练习: 学习自然语言处理时候写的一些代码 (gitee.com) 数据来源:麦卡里价格建议挑战Mercari Price Suggestion Challenge | Kaggl ...
- [ABC126E] 1 or 2
2023-01-07 题目 题目传送门 翻译 翻译 难度&重要性(1~10):2 题目来源 AtCoder 题目算法 并查集 解题思路 因为每张卡片上的数字只能是 \(1\) 或者 \(2\) ...
- P3378 【模板】二叉堆
[洛谷]P3378 [模板]堆 方法一 手写堆 最小堆插入 从新增的最后一个结点的父结点开始,用要插入元素向下过滤上层结点(相当于要插入的元素向上渗透) void siftdown(int i) // ...
- Excel中的数值四舍五入方法详解
在日常工作和数据处理中,我们经常需要对数值进行四舍五入操作.Excel作为一款强大的电子表格软件,提供了多种方法来实现数值的四舍五入.本文将介绍Excel中常用的四舍五入函数及其基本使用方法. ROU ...
- [python] 第一个爬虫, 爬妹子写真集图片
效果图 版本以及需要用到的第三方库 python 2.7 bs4 (安装命令: pip install bs4) - 代码 #!/usr/bin/env python #coding:UTF-8 #脚 ...
- OCI云主机环境如何上传下载文件
OCI云主机的连接是使用密钥而非用户密码连接. 之前使用的非主流的一个SSH工具,正常连接主机都没问题,但需要手工输入用户. 可是在选择SFTP时,始终找不到用户名的设置,导致密钥连接的SFTP始终失 ...
- 「co-examination - A」
破壁,组合意义法: 五种颜色 \(\star,a,b,c,d\). 对于 l.h.s. 钦定 \(k\),在 \(3n+k\) 个球中选出 \(2n\) 个球染色,在靠左的 \(n\) 个球中选 \( ...
- Springboot支持XML格式报文的传输
导入依赖-jackson-dataformat-xml <!--整合web模块--> <dependency> <groupId>org.springframewo ...