基于Electron27+React18+ArcoDesign搭建桌面版OS管理系统。
electron-react-mateos 基于最新前端跨端技术栈
electron27.x+react18+arco-design+zustand4+sortablejs
构建的一款仿制matePad界面多层级路由管理OS系统。
ElectronReactOS支持桌面多路由配置,新开窗口+弹窗开启路由页面。
使用技术
- 编码开发:vscode
- 框架技术:electron27+vite4+react18+zustand+react-router
- 打包工具:electron-builder^24.6.4
- 组件库:arco-design (字节react轻量级UI组件库)
- 拖拽组件:sortablejs
- 模拟请求:axios
- 弹窗组件:rdialog (基于react多功能layer弹窗)
- 美化滚动条:rscroll (基于react虚拟滚动条组件)
如何使用electron27创建多窗口,可以去看看这篇分享文章。
https://blog.csdn.net/yanxinyun1990/article/details/134047329
项目结构目录
使用vite4构建工具创建react18项目,遵循react hooks语法编码开发。
electron+react桌面布局
桌面分为顶部栏+桌面端路由菜单+底部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实现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+react18实现桌面级路由
/**
* Desk桌面多层级路由菜单
* Create by andy 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>
)
}
好了,以上就是electron+react18开发桌面os的一些知识分享。
https://blog.csdn.net/yanxinyun1990/article/details/132825719
https://blog.csdn.net/yanxinyun1990/article/details/131408928