零碎简介

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

我的项目构造如下

特色

  1. 反对PC及平板自适应布局
  2. 最新前端技术Vite2、Vue3、Electron12、Element Plus、Vue-i18n、Echarts5.x
  3. 反对组件式+指令式两种权限认证形式
  4. 反对中英文/繁体国际化计划
  5. 反对表格拖拽排序、全屏、树形表格等性能
  6. 反对多主题换肤切换

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