百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Electron13-Vue3-MacUI仿mac桌面UI后台管理框架

ccwgpt 2024-10-11 11:09 33 浏览 0 评论

这段时间一直在捣鼓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框架的介绍。希望对大家有所帮助哈!

相关推荐

自己动手写Android数据库框架_android开发数据库搭建

http://blog.csdn.net/feiduclear_up/article/details/50557590推荐理由关于Android数据库操作,由于每次都要自己写数据库操作,每次还得去...

谷歌开源大模型评测工具LMEval,打通谷歌、OpenAI、Anthropic

智东西编译|金碧辉编辑|程茜智东西5月28日消息,据科技媒体TheDecoder5月26日报道,当天,谷歌正式发布开源大模型评测框架LMEval,支持对GPT-4o、Claude3.7...

工信部:着力推动大模型算法、框架等基础性原创性的技术突破

工信部新闻发言人今日在发布会上表示,下一步,我们将坚持突出重点领域,大力推动制造业数字化转型,推动人工智能创新应用。主要从以下四个方面着力。一是夯实人工智能技术底座。通过科技创新重大项目,着力推动大模...

乒乓反复纠结“框架不稳定”的三个小误区

很多球友由于对框架的认知不清晰,往往会把“框架不稳定”当成一种心理负担,从而影响学球进度,其典型状态就是训练中有模有样,一旦进入实战,就像被捆住了手脚。通过训练和学习,结合“基本功打卡群”球友们交流发...

前AMD、英特尔显卡架构师Raja再战GPU,号称要全面重构堆栈

IT之家8月5日消息,知名GPU架构师拉贾科杜里(RajaKoduri)此前曾先后在AMD和英特尔的显卡部门担任要职。而在今日,由Raja创立的GPU软件与IP初创企...

三种必须掌握的嵌入式开发程序架构

前言在嵌入式软件开发,包括单片机开发中,软件架构对于开发人员是一个必须认真考虑的问题。软件架构对于系统整体的稳定性和可靠性是非常重要的,一个合适的软件架构不仅结构清晰,并且便于开发。我相...

怪不得别人3秒就知道软考案例怎么做能50+

软考高级统一合格标准必须三科都达到45分,案例分析也一直是考生头疼的一门,但是掌握到得分点,案例能不能50+还不是你们说了算吗?今天就结合架构案例考点,分享实用的备考攻略~一、吃透考点,搭建知识框架从...

UML统一建模常用图有哪些,各自的作用是什么?一篇文章彻底讲透

10万+爆款解析:9大UML图实战案例,小白也能秒懂!为什么需要UML?UML(统一建模语言)是软件开发的“蓝图”,用图形化语言描述系统结构、行为和交互,让复杂需求一目了然。它能:降低沟通成本避...

勒索软件转向云原生架构,直指备份基础设施

勒索软件组织和其他网络犯罪分子正越来越多地将目标对准基于云的备份系统,对久已确立的灾难恢复方法构成了挑战。谷歌安全研究人员在一份关于云安全威胁演变的报告中警告称,随着攻击者不断改进数据窃取、身份泄露和...

ConceptDraw DIAGRAM:释放创意,绘就高效办公新未来

在当今数字化时代,可视化工具已成为提升工作效率和激发创意的关键。ConceptDrawDIAGRAM,作为一款世界顶级的商业绘图软件,凭借其强大的功能和用户友好的界面,正逐渐成为众多专业人士的首选绘...

APP 制作界面设计教程:一步到位_app界面设计模板一套

想让APP界面设计高效落地,无需繁琐流程,掌握“框架搭建—细节填充—体验优化”三步法,即可一步到位完成专业级设计。黄金框架搭建是基础。采用“三三制布局”:将屏幕横向三等分,纵向保留三...

MCP 的工作原理:关键组件_mcp部件

以下是MCP架构的关键组件:MCP主机:像ClaudeDesktop、GitHubCopilot或旅行助手这样的AI智能体,它们希望通过MCP协议访问工具、资源等。MCP主机会...

软件架构_软件架构师工资一般多少

软件架构师自身需要是程序员,并且必须一直坚持做一线程序员。软件架构应该是能力最强的一群程序员,他们通常会在自身承接编程任务的同时,逐渐引导整个团队向一个能够最大化生产力的系统设计方向前进。软件系统的架...

不知不觉将手机字体调大!老花眼是因为“老了吗”?

现在不管是联系、交友,还是购物,都离不开手机。中老年人使用手机的时间也在逐渐加长,刷抖音、看短视频、发朋友圈……看手机的同时,人们也不得不面对“视力危机”——老花眼,习惯眯眼看、凑近看、瞪眼看,不少人...

8000通用汉字学习系列讲座(第046讲)

[表声母字]加(续)[从声汉字]伽茄泇迦枷痂袈笳嘉驾架咖贺瘸(计14字)嘉[正音]标准音读jiā。[辨形]上下结构,十四画。会意形声字,从壴从加,加也表声。注:从壴,字义与鼓乐有关;从加,字义与...

取消回复欢迎 发表评论: