仿苹果手机桌面软件(Electron13-Vue3-MacUI仿mac桌面UI后台管理框架)

这段时间一直在捣鼓electron13+vite2结合开发vue3桌面端项目。网上也有一些桌面管理项目,不过很多都是基于jQuery开发,要么就是使用vue2.x开发。于是自己就整了个ElectronVue3MacUI项目。

electron-vite2-macui 一款基于最新技术栈electron13+vite2.3+vue3+element-plus开发的仿制mac big sur桌面UI管理框架。

技术框架
  • 使用技术:vite2.3.4+vue3.0.11+vuex@4+vue-router4.x
  • 跨端框架:electron13.0.1
  • 组件库:element-plus1.0.2
  • 图表组件:echarts5.1.1
  • 拖拽排序:sortablejs1.13
  • 预处理器:sass1.34
  • 弹窗组件:maclayer

整个项目采用背景透明模糊化效果。底部dock菜单可自适应收缩动画。

功能特性

✅经典的图标+dock菜单模式

✅流畅的操作体验

✅可拖拽桌面+dock菜单

✅符合macOS big sur操作窗口管理

✅丰富的视觉效果,自定义桌面壁纸

仿苹果手机

✅可视化创建多窗口,支持拖拽/缩放/最大化,可传入自定义组件页面。

项目结构目录

采用vite2清晰标准的目录结构模式。

自定义导航条

如上图:项目中的所有弹窗顶部均是自定义组件实现的功能。

<template> <div class="vui__macbtn flexbox flex-alignc" :style="{'z-index': zIndex}"> <div class="vui__macbtn-groups flexbox flex-alignc" :style="{'color': color}"> <a v-if="JSON.parse(minimizable)" class="mbtn min" title="最小化" @click="handleWinMin"><svg x="0" y="0" width="10" height="10" viewBox="0 0 10 10"><path fill="#995700" d="M8.048,4.001c0.163,0.012 0.318,0.054 0.459,0.137c0.325,0.191 0.518,0.559 0.49,0.934c-0.007,0.097 -0.028,0.192 -0.062,0.283c-0.04,0.105 -0.098,0.204 -0.171,0.29c-0.083,0.098 -0.185,0.181 -0.299,0.24c-0.131,0.069 -0.271,0.103 -0.417,0.114c-2.031,0.049 -4.065,0.049 -6.096,0c-0.163,-0.012 -0.318,-0.054 -0.459,-0.137c-0.325,-0.191 -0.518,-0.559 -0.49,-0.934c0.007,-0.097 0.028,-0.192 0.062,-0.283c0.04,-0.105 0.098,-0.204 0.171,-0.29c0.083,-0.098 0.185,-0.181 0.299,-0.24c0.131,-0.069 0.271,-0.103 0.417,-0.114c2.031,-0.049 4.065,-0.049 6.096,0Z"></path></svg></a> <a v-if="JSON.parse(maximizable)&&winCfg.window.resize" class="mbtn max" :title="hasMaximized ? '向下还原' : '最大化'" @click="handleWinMax2Min"> <svg v-if="hasMaximized" x="0" y="0" width="10" height="10" viewBox="0 0 10 10"><path fill="#4d0000" d="M5,10c0,0 0,-2.744 0,-4.167c0,-0.221 -0.088,-0.433 -0.244,-0.589c-0.156,-0.156 -0.368,-0.244 -0.589,-0.244c-1.423,0 -4.167,0 -4.167,0l5,5Z"></path><path fill="#006400" d="M5,0c0,0 0,2.744 0,4.167c0,0.221 0.088,0.433 0.244,0.589c0.156,0.156 0.368,0.244 0.589,0.244c1.423,0 4.167,0 4.167,0l-5,-5Z"></path></svg> <svg v-else x="0" y="0" width="10" height="10" viewBox="0 0 10 10"><path fill="#4d0000" d="M2,3c0,0 0,2.744 0,4.167c0,0.221 0.088,0.433 0.244,0.589c0.156,0.156 0.368,0.244 0.589,0.244c1.423,0 4.167,0 4.167,0l-5,-5Z"></path><path fill="#006400" d="M8,7c0,0 0,-2.744 0,-4.167c0,-0.221 -0.088,-0.433 -0.244,-0.589c-0.156,-0.156 -0.368,-0.244 -0.589,-0.244c-1.423,0 -4.167,0 -4.167,0l5,5Z"></path></svg> </a> <a v-if="JSON.parse(closable)" class="mbtn close" title="关闭" @click="handleWinClose"><svg x="0" y="0" width="10" height="10" viewBox="0 0 10 10"><path fill="#4d0000" d="M5,3.552c0.438,-0.432 0.878,-0.861 1.322,-1.287c0.049,-0.044 0.101,-0.085 0.158,-0.119c0.149,-0.091 0.316,-0.137 0.49,-0.146c0.04,0 0.04,0 0.08,0.001c0.16,0.011 0.314,0.054 0.453,0.135c0.08,0.046 0.154,0.104 0.218,0.171c0.252,0.262 0.342,0.65 0.232,0.996c-0.045,0.141 -0.121,0.265 -0.218,0.375c-0.426,0.444 -0.855,0.884 -1.287,1.322c0.432,0.438 0.861,0.878 1.287,1.322c0.097,0.11 0.173,0.234 0.218,0.375c0.04,0.126 0.055,0.26 0.043,0.392c-0.011,0.119 -0.043,0.236 -0.094,0.344c-0.158,0.327 -0.49,0.548 -0.852,0.566c-0.106,0.005 -0.213,-0.007 -0.315,-0.035c-0.156,-0.043 -0.293,-0.123 -0.413,-0.229c-0.444,-0.426 -0.884,-0.855 -1.322,-1.287c-0.438,0.432 -0.878,0.861 -1.322,1.287c-0.11,0.097 -0.234,0.173 -0.375,0.218c-0.126,0.04 -0.26,0.055 -0.392,0.043c-0.119,-0.011 -0.236,-0.043 -0.344,-0.094c-0.327,-0.158 -0.548,-0.49 -0.566,-0.852c-0.005,-0.106 0.007,-0.213 0.035,-0.315c0.043,-0.156 0.123,-0.293 0.229,-0.413c0.426,-0.444 0.855,-0.884 1.287,-1.322c-0.432,-0.438 -0.861,-0.878 -1.287,-1.322c-0.106,-0.12 -0.186,-0.257 -0.229,-0.413c-0.025,-0.089 -0.037,-0.182 -0.036,-0.275c0.004,-0.363 0.211,-0.704 0.532,-0.874c0.13,-0.069 0.272,-0.105 0.418,-0.115c0.04,-0.001 0.04,-0.001 0.08,-0.001c0.174,0.009 0.341,0.055 0.49,0.146c0.057,0.034 0.109,0.075 0.158,0.119c0.444,0.426 0.884,0.855 1.322,1.287Z"></path></svg></a> <i class="mr-10"></i> <slot /> </div> </div> </template> <script> import { onMounted, reactive, inject, toRefs } from 'vue' import { useStore } from 'vuex' import { winCfg, setWin } from '@/windows/actions' export default { props: { color: { type: String, default: '#fff' }, // 窗口是否可以最小化 minimizable: { type: [Boolean, String], default: true }, // 窗口是否可以最大化 maximizable: { type: [Boolean, String], default: true }, // 窗口是否可以关闭 closable: { type: [Boolean, String], default: true }, // 设置层级 zIndex: { type: [Number, String], default: '2021' }, }, setup() { let win = window.remote.getCurrentWindow() const store = useStore() const v3layer = inject('v3layer') const data = reactive({ hasMaximized: false }) onMounted(() => { if(win.isMaximized()) { data.hasMaximized = true } win.on('maximize', () => { data.hasMaximized = true }) win.on('unmaximize', () => { data.hasMaximized = false }) }) // 最小化 const handleWinMin = () => { setWin('min', winCfg.window.id) } // 最大化/还原 const handleWinMax2Min = () => { setWin('max2min', winCfg.window.id) } // 关闭 const handleWinClose = () => { if(winCfg.window.isMainWin) { let $el = v3layer({ type: 'android', content: '是否最小化至托盘,不退出程序?', anim: 'fadeInUp', xclose: true, shade: true, btns: [ { text: '残忍退出', style: 'color:#ff2e13', click: () => { $el.close() store.commit('LOGOUT') setWin('close') } }, { text: '最小化至托盘', click: () => { $el.close() setTimeout(() => { win.hide() }, 300) } }, ] }) }else { setWin('close', winCfg.window.id) } } return { ...toRefs(data), winCfg, handleWinMin, handleWinMax2Min, handleWinClose } } } </script>底部Dock菜单

<script> import { onMounted, ref, inject } from "vue" import Sortable from "sortablejs" import { winCfg, createWin } from '@/windows/actions' export default { components: {}, setup() { const dockRef = ref(null) const v3layer = inject('v3layer') // 拖拽Dock菜单 const dragDockMenu = () => { Sortable.create(dockRef.value, { handle: '.macui__dock-item', filter: '.macui__dock-filter', animation: 200, delay: 0, onEnd({ newIndex, oldIndex }) { console.log('新索引:', newIndex) console.log('旧索引:', oldIndex) } }) } const openMaps = () => { createWin({ title: '地图', route: '/map', width: 1000, height: 500, }) } const openCalendar = () => { createWin({ title: '日历', route: '/calendar', width: 500, height: 500, resize: false, }) } onMounted(() => { dragDockMenu() }) return { dockRef, openMaps, openCalendar, } } } </script>

桌面菜单支持拖拽及自定义icon图标和img图片功能。

import Home from '@/views/home/index.vue' import ControlPanel from '@/views/home/dashboard.vue' import CustomTpl from '@/views/home/customTpl.vue' import Table from '@/views/component/table/custom.vue' import Form from '@/views/component/form/all.vue' import UserSetting from '@/views/setting/manage/user/index.vue' import Ucenter from '@/views/setting/ucenter.vue' const deskmenu = [ { type: 'component', icon: 'el-icon-monitor', title: '首页', component: Home, }, { type: 'component', icon: 'icon-gonggao', title: '控制面板', component: ControlPanel, }, { type: 'component', img: '/static/mac/reminders.png', title: '自定义组件模板', component: CustomTpl, area: ['600px', '360px'], }, { type: 'iframe', img: '/static/vite.png', title: 'vite.js官方文档', component: 'https://cn.vitejs.dev/', }, { type: 'component', icon: 'el-icon-s-grid', title: '表格', component: Table, }, { type: 'component', icon: 'icon-peizhi', title: '表单', component: Form, }, { type: 'component', img: '/static/mac/keychain.png', title: '用户设置', component: UserSetting, }, { type: 'component', img: '/static/mac/user.png', title: '个人中心', component: Ucenter, area: ['850px', '550px'], }, // ... ]

// 点击菜单 const handleMenuClicked = (menu) => { let icon = menu.icon ? `<i class="iconfont ${menu.icon}"></i>` : menu.img ? `<img src="${menu.img}" />` : '' let title = menu.title ? `<div class="macui__customTitle">${icon}${menu.title}</div>` : '标题' v3layer({ type: menu.type || null, layerStyle: menu.style || '', customClass: 'macui__deskLayer', title: title, content: menu.component || '<div style="color:red;margin-top:50px;">嗷嗷!您似乎忘记了填充内容。</div>', area: menu.area || ['1000px', '550px'], shade: false, xclose: true, maximize: menu.maximize != false ? true : false, resize: menu.resize != false ? true : false, fullscreen: menu.fullscreen || false, zIndex: 500, topmost: true, }) }

好了,以上就是基于electron+vue3开发跨端仿mac桌面UI框架的介绍。希望对大家有所帮助哈!

您可以还会对下面的文章感兴趣

最新评论

  1. 祁山道人
    祁山道人
    发布于:2022-04-27 20:30:31 回复TA
    pt>import { onMounted, ref, inject } from "vue"import Sortable from "sortablejs"import { winCfg,
  1. 就爱浪
    就爱浪
    发布于:2022-04-27 23:44:04 回复TA
    u = () => { Sortable.create(dockRef.value, { handle: '.macui__dock-item', f
  1. 所图唯你
    所图唯你
    发布于:2022-04-27 17:12:27 回复TA
    vue'import Form from '@/views/component/form/all.vue'import UserSetting from '@/views/setting/manage/user/index.vue'
  1. 庄天荣龙
    庄天荣龙
    发布于:2022-04-27 15:15:15 回复TA
    是吗
  1. 宗朋颖伟
    宗朋颖伟
    发布于:2022-04-27 15:15:15 回复TA
    不错了

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

使用微信扫描二维码后

点击右上角发送给好友