Electron-Vue3-Vadmin后台系统|vite2+electron桌面端权限管理系统
基于vite2.x+electron12桌面端后台管理系统Vite2ElectronVAdmin。
继上一次分享vite2整合electron搭建后台框架,这次带来的是最新开发的跨桌面中后台权限管理系统。使用最新的前端技术栈,内置 i18n 国际化解决方案,动态权限路由,权限验证,整合了典型的表格/表单等业务模块功能。

一、技术栈
- 编码器:vscode
- vue3技术:vite2.1.5+vue3.0+vuex4+vue-router@4
- 跨端框架:electron^12.0.4
- 打包工具:vue-cli-plugin-electron-builder
- UI组件库:element-plus^1.0.2 (饿了么vue3组件库)
- 表格拖拽:sortablejs^1.13.0
- 图表组件:echarts^5.1.1
- 国际化方案:vue-i18n^9.1.6
- 数据模拟:mockjs^1.1.0

二、主要特性
- 前端技术栈Vite2、Vue3、Electron12、Element Plus、Vue-i18n、Echarts5.x、Sortable、Mockjs。
- 权限认证支持组件式+指令式两种方式。
- 支持中文/英文/繁体国际化解决方案。
- 支持表格拖拽排序、缩放、树形表格等功能。
- 支持加载动态权限菜单,多方式轻松权限控制。
- 高效率开发,整个框架已经搭建完毕,只需新增相应模块即可。

三、项目结构图

整个项目使用最新vue3语法编码,采用标准的分层目录结构形式,数据均是使用Mock.js进行模拟。




















◆ electron支持多开新窗口
项目支持打开多个窗口,如主题换肤、关于等窗口。只需通过如下的方式调用即可。
import { winCfg, createWin } from '@/windows/actions'
// 换肤窗口
const handleOpenTheme = () => {
createWin({
title: '个性装扮',
route: '/skin',
width: 750,
height: 480,
modal: true,
parent: winCfg.window.id,
resize: false,
})
}
大家如果对electron创建多窗口模式感兴趣的话,可以去看看下面这篇文章。
https://www.cnblogs.com/xiaoyan2017/p/14403820.html
◆ electron实现无边框Mac导航栏效果





如上图:顶部导航栏默认是Mac风格,也支持自定义标题、背景/文字颜色、是否沉浸式透明背景等功能。
通过设置 -webkit-app-region: drag 实现导航条可拖拽,标题及按钮设置 -webkit-app-region:no-drag 可响应点击事件。
<!-- //顶部导航 -->
<template>
<WinBar zIndex="1000">
<template #wbtn>
<MsgMenu />
<Lang />
<a class="wbtn" title="换肤" @click="handleSkinWin"><i class="iconfont icon-huanfu"></i></a>
<Setting />
<a class="wbtn" title="刷新" @click="handleRefresh"><i class="iconfont el-icon-refresh"></i></a>
<a class="wbtn" :class="{'on': isAlwaysOnTop}" :title="isAlwaysOnTop ? '取消置顶' : '置顶'" @click="handleAlwaysTop"><i class="iconfont icon-ding"></i></a>
<Avatar @logout="handleLogout" />
</template>
</WinBar>
</template>
对于自定义导航条的实现方式,由于之前有过相关分享文章,这里就不详细介绍了。
https://www.cnblogs.com/xiaoyan2017/p/14449570.html
◆ Vite2|electron项目布局模板
为了使得项目分层结构更加清晰,布局分为 Auth 和 Main 两大模块。

<!-- //Auth主模块模板 -->
<template>
<div class="vadmin__wrapper">
<router-view class="vadmin__layouts-auth"></router-view>
</div>
</template> <script>
import { useRoute } from "vue-router"
import useTitle from '@/hooks/useTitle' export default {
components: {},
setup() {
const route = useRoute() // 设置标题
useTitle(route)
}
}
</script>
<!-- //Main主模块模板 -->
<template>
<div class="vadmin__wrapper" :style="{'--themeSkin': store.state.skin}">
<div v-if="!route.meta.isNewin" class="vadmin__layouts-main flexbox flex-col">
<!-- //顶部导航 -->
<div class="layout__topbar">
<TopNav />
</div> <div class="layout__workpanel flex1 flexbox">
<!-- //侧边栏 -->
<div v-show="rootRouteEnable" class="panel__leftlayer">
<SideMenu :routes="mainRoutes" :rootRoute="rootRoute" />
</div> <!-- //中间栏 -->
<div class="panel__middlelayer" :class="{'collapsed': collapsed}">
<RouteMenu
:routes="getAllRoutes"
:rootRoute="rootRoute"
:defaultActive="defaultActive"
:rootRouteEnable="rootRouteEnable"
/>
</div> <!-- //右边栏 -->
<div class="panel__rightlayer flex1 flexbox flex-col">
<!-- 面包屑导航 -->
<BreadCrumb /> <!-- 主内容区 -->
<v3-scroll autohide>
<div class="lay__container">
<!-- //路由权限控制 -->
<permission :roles="route.meta.roles">
<template #tooltips>
<Forbidden />
</template>
<router-view></router-view>
</permission>
</div>
</v3-scroll>
</div>
</div>
</div>
<router-view v-else class="vadmin__layouts-main flexbox flex-col"></router-view>
</div>
</template>
◆ Vue-Router路由配置
/**
* 路由配置 Router util
* @author XiaoYan
*/ import { createRouter, createWebHashHistory } from "vue-router" import { ElLoading } from "element-plus"
import { loginWin } from "@/windows/actions" import store from '@/store' // 导入公共模板/路由配置
import mainLayout from "@/layouts/main"
import authLayout from "@/layouts/auth"
import mainRoutes from "@/layouts/main/routes.js"
import authRoutes from "@/layouts/auth/routes.js" const RoutesLs = [
// 主页面模块
{
path: '/',
redirect: '/home/index',
component: mainLayout,
children: mainRoutes,
},
// 验证模块
{
path: '/auth',
redirect: '/auth/login',
component: authLayout,
children: authRoutes,
},
// 错误模块
{
path: '/:pathMatch(.*)*',
component: () => import('@/views/error/404.vue'),
meta: {
title: 'app__global-page-notfound',
}
}
] const router = createRouter({
history: createWebHashHistory(),
routes: RoutesLs,
}) // 全局钩子拦截验证状态
let loadingIns
router.beforeEach((to, from, next) => {
// 开启加载提示
loadingIns = ElLoading.service({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(19, 209, 122, .1)'
}) // 判断当前路由是否需要验证状态
const isLogined = store.state.isLogin
if(to.meta.auth) {
if(isLogined) {
next()
}else {
loginWin()
loadingIns.close()
}
}else {
next()
}
}) router.afterEach(() => {
// 关闭加载提示
loadingIns.close()
})
◆ Vue-I18n国际化解决方案
项目中路由采用了 vue-i18n 国际化,支持中文|繁体|英文三种语言。

目前vue-i18n插件支持vue3项目了,大家需安装最新版本即可。
npm i vue-i18n@next -D

如上图:新建locale目录用来处理相应模块语言配置。
import { createI18n } from "vue-i18n"
import Storage from "@/utils/storage"
// 默认值
export const langKey = 'lang'
export const langVal = 'zh-CN'
/* elementPlus国际化配置 */
import enUS from "element-plus/lib/locale/lang/en"
import zhCN from "element-plus/lib/locale/lang/zh-cn"
import zhTW from "element-plus/lib/locale/lang/zh-tw"
export const elPlusLang = {
'en-US': enUS,
'zh-CN': zhCN,
'zh-TW': zhTW,
}
/* 初始化多语言 */
export const $messages = importAllLang()
export const $lang = getLang()
const i18n = createI18n({
legacy: false,
locale: $lang,
messages: $messages
})
◆ 动态化图表Hooks
项目中图表是使用最新的Echarts组件。为了避免每次都使用echarts.init调用图表接口。于是就封装了调用图表hook函数。
一开始是使用监听window.resize来自适应图表尺寸,这里有一个bug,只有窗口大小改变才会触发,而DOM改变则不会触发了,于是改用 element-resize-detector 来监听,完美解决问题。
/**
* 动态化图表Hook
* @author XiaoYan
*/ import { onMounted, onBeforeUnmount, ref } from "vue"
import * as echarts from "echarts"
import elementResizeDetectorMaker from "element-resize-detector"
import utils from "@/utils" export default function useChart(refs, options) {
let chartInst
let chartRef = ref(null)
let erd = elementResizeDetectorMaker() const handleResize = utils.debounce(() => {
chartInst.resize()
}, 100) onMounted(() => {
if(refs.value) {
chartInst = echarts.init(refs.value)
chartInst.setOption(options)
chartRef.value = chartInst
}
// window.addEventListener('resize', handleResize)
erd.listenTo(refs.value, handleResize)
}) onBeforeUnmount(() => {
chartInst.dispose()
// window.removeEventListener('resize', handleResize)
erd.removeListener(refs.value, handleResize)
}) return chartRef
}

◆ 路由权限管理
项目中的权限尺寸组件式和指令式两种模式。
<!-- //权限验证模板 -->
<template>
<slot v-if="isPermission"/>
<slot v-else name="tooltips">
<el-alert title="对不起,您没有权限操作此页面!" type="error" show-icon></el-alert>
</slot>
</template>
<script>
import { computed } from "vue";
import { useStore } from "vuex";
import { getPermissionRoute } from "@/utils/routes";
export default {
props: { roles: { type: [String, Array] } },
components: {},
setup(props) {
const store = useStore();
// 判断是否有权限
const isPermission = computed(() =>
getPermissionRoute(JSON.stringify(store.state.roles), props.roles)
);
return { isPermission };
}
};
</script>
import store from "@/store"
import { getPermissionRoute } from "@/utils/routes" const Permission = (el, binding) => {
const { value } = binding
if(value) {
const userRoles = JSON.stringify(store.state.roles)
if(!getPermissionRoute(userRoles, value)) {
el.parentNode && el.parentNode.removeChild(el)
}
}else {
console.error(`Set Roles! Like v-permission="['admin', 'dev']" or v-permission="'test'"`)
}
} export default Permission
组件调用
<Permission roles="test">
<template #tooltips>
<h2 style="color:red;">此模块只有test角色才能操作!</h2>
</template>
<el-button type="primary" icon="el-icon-search">查询</el-button>
</Permission> <el-divider /> <Permission roles="dev">
<template #tooltips>
<h2 style="color:red;">你无权操作Dev模块!</h2>
</template>
<el-button type="primary" icon="el-icon-edit">编辑</el-button>
<el-button type="warning" icon="el-icon-delete">删除</el-button>
</Permission>
指令调用
<el-button v-permission="'test'" type="primary" icon="el-icon-search">查询</el-button>
<el-button v-permission="'dev'" type="success" icon="el-icon-plus">新增</el-button>
<el-button v-permission="['test', 'dev']" type="warning" icon="el-icon-edit">编辑</el-button>
<el-button v-permission="['admin']" type="danger" icon="el-icon-delete">删除</el-button>
◆ electron-builder打包配置
{
"productName": "electron-vadmin",
"appId": "cc.xiaoyan.electron-vadmin",
"copyright": "Copyright 2021-present XiaoYan",
"compression": "maximum",
"asar": false,
"extraResources": [{
"from": "./resource","to": "resource"
}],
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"perMachine": true,
"deleteAppDataOnUninstall": true,
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"shortcutName": "ElectronVAdmin"
},
"win": {
"icon": "./resource/shortcut.ico",
"artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}",
"target": [{
"target": "nsis","arch": ["ia32"]
}]
},
"mac": {
"icon": "./resource/shortcut.icns","artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"
},
"linux": {
"icon": "./resource","artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"
}
}
最后还需注意
1、项目路径命名不能包含中文,否则打包会报错!
2、尽量不要使用 getCurrentInstance 函数来使用router或store,打包也会报错!
3、打包后运行出现白屏情况,可配置 history: createWebHashHistory()
4、提示fs.existsSync错误,设置nodeIntegration: true开启Node支持;

好了,基于vite2+electron开发后台管理系统就分享到这里,希望对大家有些帮助~
最后附上一个electron+vue3+vant短视频项目
https://www.cnblogs.com/xiaoyan2017/p/14585223.html

Electron-Vue3-Vadmin后台系统|vite2+electron桌面端权限管理系统的更多相关文章
- 分享一个基于ligerui的系统应用案例ligerRM V2(权限管理系统)(提供下载)
阅读目录 简介 系统特色 系统介绍 - 首页 系统介绍 - 列表页 系统介绍 - 明细页(表单) 系统介绍 - 菜单/按钮 系统介绍 - 权限中心 系统介绍 - 数据权限 系统介绍 - 字段权限 系统 ...
- 基于vite2+electron12后台管理模板|Electron后台框架系统
前一溜时间有给大家分享一个 electron+vite跨端短视频 项目.这次分享的是vite2.x和electron实现跨平台后台框架,支持国际化多语言配置.导航菜单+树形菜单两种路由菜单模式.展开/ ...
- Vite2+Electron仿抖音|vite2.x+electron12+vant3短视频|直播|聊天
整合vite2+electron12跨平台仿抖音电脑版实战Vite2-ElectronDouYin. 基于vite2.0+electron12+vant3+swiper6+v3popup等技术跨端仿制 ...
- 快速了解Electron:新一代基于Web的跨平台桌面技术
本文引用了作者“ ConardLi”的<用JS开发跨平台桌面应用,从原理到实践>一文部分内容,原文链接:segmentfault.com/a/1190000019426512,感谢原作者的 ...
- 【electron+vue3+ts实战便笺exe】一、搭建框架配置
不要让自己的上限成为你的底线 前言 诈尸更新系列,为了跟上脚步,尝试了vue3,在学习vue3的时候顺便学习一手electron和ts,本教程将分别发布,源码会在最后的文章发布.因为还在开发中,目前也 ...
- Vite ❤ Electron——基于Vite搭建Electron+Vue3的开发环境【一】
背景 目前社区两大Vue+Electron的脚手架:electron-vue和vue-cli-plugin-electron-builder, 都有这样那样的问题,且都还不支持Vue3,然而Vue3已 ...
- 【electron+vue3+ts实战便笺exe】二、electron+vue3开发内容
不要让自己的上限成为你的底线 本来以为有万字的..没想到才堪堪近6000字.为了水文的嫌疑,只挑了重点的地方讲,比如component内的组件就挑了右键弹窗去说明,建议在看本文的时候边查看项目,有不懂 ...
- 【原创】从零开始搭建Electron+Vue+Webpack项目框架(六)Electron打包,同时构建客户端和web端
导航: (一)Electron跑起来(二)从零搭建Vue全家桶+webpack项目框架(三)Electron+Vue+Webpack,联合调试整个项目(四)Electron配置润色(五)预加载及自动更 ...
- 【Electron】在 WSL2 中 打包 electron Linux 版本
[Electron]在 WSL2 中 打包 electron Linux 版本. 安装 WSL 我使用的是 Ubuntu 20.04.4 LTS 的版本. 安装 WSL 文档地址:https://do ...
随机推荐
- python-类的隐藏和封装
7 """ 8 封装是面对对象的三大特征之一(另外两个是集成和多态),它指的是将对象> 的信息隐藏在对象的内部,不允许外部程序直接访问对象内部信息,而是通> ...
- HTML的基础语法
区别于c语言这类高级语言,HTML不是编程语言,而好似一种描述型语言,用于描述网页中内容的显示方式. HTML标记以<>来进行标记.HTML中的标记按其是否成对出现,可以分为单标记和双标记 ...
- Lambda 表达式(使用前提、“类型推断”、作用、优缺点、Lambda还能省略的情况)
Lambda 表达式(使用前提."类型推断".作用.优缺点.Lambda还能省略的情况) 1.Lambda使用前提: (1)使用Lambda必须有接口,且接口只有一个抽象方法(即函 ...
- codefoces B - Phoenix and Beauty
原题链接:https://codeforc.es/problemset/problem/1348/B 题意:告诉我们一个数组及其长度和k,判断是否可以构造一个新数组使得每K段长度和都相等. 思路:首先 ...
- P1996_约瑟夫问题(JAVA语言)_可能是最简单的解法了!
思路:使用队列模拟. 判断是否为出圈的数.如果不是,把数加入队列尾部:如果是,输出并删除. 题目背景 约瑟夫是一个无聊的人!!! 题目描述 n个人(n<=100)围成一圈,从第一个人开始报数,数 ...
- DAOS 分布式异步对象存储|存储模型
概述 DAOS Pool 是分布在 Target 集合上的存储资源预留.分配给每个 Target 上的 Pool 的实际空间称为 Pool Shard. 分配给 Pool 的总空间在创建时确定,后期可 ...
- [源码解析] 并行分布式框架 Celery 之 worker 启动 (2)
[源码解析] 并行分布式框架 Celery 之 worker 启动 (2) 目录 [源码解析] 并行分布式框架 Celery 之 worker 启动 (2) 0x00 摘要 0x01 前文回顾 0x2 ...
- 【死磕JVM】给同事讲了一遍GC后,他要去面试,年轻人,就是容易冲动!
前言 在一个风和日丽的中午,和同事小勇一起走在公司楼下的小公园里面,看到很多的小姐姐,心想什么时候能够和这些小姐姐一起讨论人生呀,美滋滋,嘿嘿嘿. 收起你的哈喇子好不好,小勇总是在这个时候发出声音,挺 ...
- Dynamics CRM安装教程六:CRM产品安装
接下来就要开始进行CRM产品的安装了 首先要安装IIS,以及.NET FrameWork4.6及相关功能 打开服务器管理器,在添加角色功能向导中勾选IIS,点击添加功能 默认下一步 选择角色服务这里的 ...
- Leedcode算法专题训练(分治法)
归并排序就是一个用分治法的经典例子,这里我用它来举例描述一下上面的步骤: 1.归并排序首先把原问题拆分成2个规模更小的子问题. 2.递归地求解子问题,当子问题规模足够小时,可以一下子解决它.在这个例子 ...