零碎简介
vite2-electron-admin 一套轻量级的中后盾零碎解决方案。它整合了vite.js + electron12
开发实现。应用了最新的前端技术栈,蕴含了 i18n 国际化语言,动静权限路由,提供了多功能表格/表单
业务性能。
技术框架
- vue3技术:vite.js+vue3.0+vuex4+vue-router@4
- 跨端框架:electron^12.0.4
- 打包工具:vue-cli-plugin-electron-builder
- UI组件库:element-plus (饿了么vue3组件库)
- 表格拖拽:sortablejs
- 图表组件:echarts^5.1.1
- 国际化:vue-i18n^9.1.6
- 模仿数据:mockjs
我的项目构造如下
特色
- 反对PC及平板自适应布局
- 最新前端技术Vite2、Vue3、Electron12、Element Plus、Vue-i18n、Echarts5.x
- 反对组件式+指令式两种权限认证形式
- 反对中英文/繁体国际化计划
- 反对表格拖拽排序、全屏、树形表格等性能
- 反对多主题换肤切换
electron-vite-admin 还反对新开多窗口,以如下换肤窗口为例,后面曾经有过相干分享文章,这里就不具体介绍。
import { createWin } from '@/windows/actions'// 换肤窗口const handleOpenTheme = () => { createWin({ title: '主题换肤', route: '/skin', width: 750, height: 480, })}
electron+vite仿mac格调导航栏
如上图:为了我的项目UI一致性,顶部导航栏采纳了自定义组件实现,格调有些相似mac导航。
<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>
至于具体的实现形式,这里不作过多介绍了。之前有过一些相干分享文章。
入口页main.js
/** * 渲染过程主入口 * @author XiaoYan */import { createApp } from 'vue'import App from './App.vue'import Router from './router'import Store from './store'// 引入公共配置import gPlugins from './plugins'import { winCfg, loadWin } from './windows/actions'loadWin().then(config => { winCfg.window = config createApp(App).use(Router).use(Store).use(gPlugins).mount('#app')})
引入公共组件plugin.js
/** * 公共组件/插件配置文件 * @author XiaoYan */// 引入公共款式import "@/assets/fonts/iconfont.css"import "@/assets/css/common.scss"// 引入elementPlus组件库import ELPlus from "element-plus"// 引入国际化配置import VueI18n, { elPlusLang, getLang } from './i18n'// 引入vue3自定义组件import V3Layer from '@/components/v3layer'import V3Scroll from '@/components/v3scroll'// 引入公共组件模板import WinBar from '@/components/winbar'import WinBtn from '@/components/winbar/winbtn.vue'import MacBtn from '@/components/winbar/macbtn.vue'import Icon from '@/components/Icon'import Utils from '@/utils'import ElUtil from './elUtil'const gPlugins = (app) => { app.use(ELPlus, { size: 'small', locale: elPlusLang[getLang()] }) app.use(VueI18n) app.use(V3Layer) app.use(V3Scroll) // 注册公共组件 app.component('WinBar', WinBar) app.component('WinBtn', WinBtn) app.component('MacBtn', MacBtn) app.component('Icon', Icon) // 注入全局依赖 app.provide('utils', Utils) app.provide('elUtil', ElUtil)}
国际化配置 vue-i18n
新建一个i18n.js用来引入国际化语言配置。
/** * 国际化配置 VueI18n util * @author XiaoYan Q:282310962 */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})/* 获取语言 */export function getLang() { const lang = Storage.get(langKey) return lang || langVal}/** * 长久化存储 * @param lang 语言类型 zh-CN / zh-TW / en-US */export function setLang(lang, reload = false) { if(getLang() !== lang) { Storage.set(langKey, lang || '') // 设置全局语言 // i18n.global.locale.value = lang // 重载页面 if(reload) { window.location.reload() } }}/** * 自动化导入本地locale目录下语言配置 */export function importAllLang() { const langModule = {} try { const localeCtx = require.context('@/locale', true, /([a-z]{2})-?([A-Z]{2})?\.js$/) localeCtx.keys().map(path => { const pathCtx = localeCtx(path) if(pathCtx.default) { const pathName = path.replace(/(.*\/)*([^.]+).*/ig, '$2') if(langModule[pathName]) { langModule[pathName] = { ...langModule[pathName], ...pathCtx.default } }else { langModule[pathName] = pathCtx.default } } }) } catch (error) { console.log(error) } return langModule}
我的项目模板布局
我的项目布局分为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>
图表Hook
为了简化图表调用,封装一个图表hooks。应用到了element-resize-detector来监测dom尺寸变动。
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 } erd.listenTo(refs.value, handleResize) }) onBeforeUnmount(() => { chartInst.dispose() erd.removeListener(refs.value, handleResize) }) return chartRef}
这样每次调用的时候,只需传入图表dom元素和图表数据即可疾速生成一个图表。
Okey,使用electron+vite.js跨端开发后盾管理系统临时分享到这里。
最初附上Electron+Vue3桌面端仿都有短视频+直播
https://segmentfault.com/a/1190000039725671