xiaoyan2017

前一溜时间有给大家分享一个 electron+vite跨端短视频 项目。这次分享的是vite2.xelectron实现跨平台后台框架,支持国际化多语言配置、导航菜单+树形菜单两种路由菜单模式、展开/收缩路由菜单等功能。

vite2-electron-system 后台模板框架使用到的版本:vite2.1.51、vue3.0.5、electron12.0.4

  • vite2.x|vue3-i18n国际化多语言

项目支持如下图3种语言切换[中文/英文/繁体],使用Vue I18n国际化vue3版本。

// 安装vue-i18n插件
npm i vue-i18n@next -D

在布局模板中新建locale多语言配置文件。

/**
 * @desc 国际化语言配置(主模板)
 */

export default {
    \'layout__main-menu__home\': \'首页\',
    \'layout__main-menu__home_dashboard\': \'控制台\',
    \'layout__main-menu__home_breadcrumbs\': \'自定义面包屑导航\',
    \'layout__main-menu__home_breadcrumbs-link\': \'https://cn.vitejs.dev/\',
    \'layout__main-menu__home_docs\': \'自定义链接\',
    \'layout__main-menu__home_tree\': \'树形菜单\',

    \'layout__main-menu__component\': \'组件\',
    \'layout__main-menu__component_table\': \'表格\',
    \'layout__main-menu__component_table-all\': \'所有表格\',
    \'layout__main-menu__component_table-custom\': \'自定义表格\',
    \'layout__main-menu__component_table-search\': \'表格搜索\',
    \'layout__main-menu__component_table-search-list\': \'搜索列表\',
    \'layout__main-menu__component_form\': \'表单\',
    \'layout__main-menu__component_form-all\': \'所有表单\',
    \'layout__main-menu__component_form-custom\': \'自定义表单\',
    \'layout__main-menu__component_editor\': \'富文本编辑器\',
}

在plugins目录下新建一个i18n.js配置文件。

/**
 * vue-i18n国际化配置文件
 */

import { createI18n } from \'vue-i18n\'
import Storage from \'@/utils/storage\'

// 默认设置
export const langKey = \'lang\'
export const langVal = \'zh-CN\'

/**
 * 引入element-plus国际化包
 */
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 = importLang()
export const $lang = getLang()
const i18n = createI18n({
    legacy: false,
    locale: $lang,
    messages: $messages
})

/**
 * 自动导入项目目录下语言配置
 */
export function importLang() {
    const localeModule = {}
    try {
        // 导入 @/layouts 文件夹下包含子目录locale中的xxx.js文件
        const layoutsCtx = require.context(\'@/layouts\', true, /[/\\]locale[/\\]([a-z]{2})-?([A-Z]{2})?\.js$/)
        layoutsCtx.keys().map(path => {
            const pathCtx = layoutsCtx(path)
            if(pathCtx.default) {
                const pathName = path.replace(/(.*\/)*([^.]+).*/ig, \'$2\')
                if(localeModule[pathName]) {
                    localeModule[pathName] = {
                        ...localeModule[pathName], ...pathCtx.default
                    }
                }else {
                    localeModule[pathName] = pathCtx.default
                }
            }
        })
    } catch (error) {
        console.log(error)
    }

    return localeModule
}

/**
 * 存储设置语言
 * @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()
        }
    }
}

/**
 * 获取语言
 */
export function getLang() {
    const lang = Storage.get(langKey)
    return lang || langVal
}

export default i18n

然后在main.js中导入配置。

// 引入element-plus组件库
import ElPlus from \'element-plus\'

// 引入多语言
import VueI18n, { ElPlusLang, getLang } from \'@/plugins/i18n\'

app.use(ElPlus, {
    size: \'small\',
    locale: ElPlusLang[getLang()]
})
app.use(VueI18n)

 

  • vite2|vue3.0动态Hook设置标题

如下图:项目中页面路由跳转,动态化显示标题。

vue3 hook 就能快速实现动态切换,在hook目录新建一个useTitle.js文件。

/**
 * 动态获取路由标题
 */

import { onMounted, watchEffect } from \'vue\'
import { useI18n } from \'vue-i18n\'

export default function useTitle(route) {
    console.log(route)
    if(!route.meta) return

    const { t } = useI18n()
    const defaultTitle = \'ELECTRON-VUE3-VADMIN\'

    const Title = () => {
        if(route.meta.title) {
            document.title = `${t(route.meta.title)} - ${defaultTitle}`
        }else {
            document.title = defaultTitle
        }
    }

    watchEffect(Title)

    onMounted(() => {
        Title()
    })
}

调用非常简单,通过如下方式即可快速实现路由地址切换标题。

import { useRoute } from \'vue-router\'
import useTitle from \'@/hooks/useTitle\'

export default {
    setup() {
        const route = useRoute()

        // 设置title
        useTitle(route)

        return {
            // ...
        }
    }
}
  • vite2.x路由载等待效果

为了避免出现等待白屏的情况,可以在路由跳转的时候加入loading提示。

使用了element-plus的loading组件。

let loadingInstance
// 全局钩子拦截登录状态
router.beforeEach((to, from, next) => {

    // 加载提示(避免白屏等待)
    // 可以使用NProgress组件:https://ricostacruz.com/nprogress/
    loadingInstance = ElLoading.service({
        lock: true,
        text: \'Loading\',
        spinner: \'el-icon-loading\',
        background: \'rgba(255, 255, 255, 0.7)\'
    })

    const hasLogined = store.state.isLogin

    // 判断当前路由地址是否需要登录权限
    if(to.meta.auth) {
        if(hasLogined) {
            next()
        }else {
            // 跳转登录页面
            loginWin()
        }
    }else {
        next()
    }
})
router.afterEach(() => {
    // 关闭加载提示
    loadingInstance.close()
})

大家根据需要也可以选择一款非常小巧强大的NProgress插件实现加载效果。

https://ricostacruz.com/nprogress/

  • vite2+element-plus路由菜单

项目中使用了elementUI导航菜单 el-menu 和树形菜单 el-tree 两种实现路由地址菜单。

<!-- el-menu导航菜单路由 -->
<div>
    <el-menu 
        :default-active="defaultActive" 
        :collapse="collapsed" 
        :collapse-transition="false" 
        class="mainLayout__menuNav" 
        @select="handleNodeSelect"
    >
        <sidebar-item 
            v-for="route in allRoutes" 
            :key="route.path" 
            :item="route" 
            :isNavEnable="isNavEnable" 
            :rootsRoute="rootsRoute" 
        />
    </el-menu>
</div>

<!-- el-tree树形菜单路由 -->
<el-tree 
    ref="treeRef"
    :data="allRoutes" 
    :props="defaultProps" 
    @node-click="handleNodeSelect"
    node-key="path"
    :default-expanded-keys="[rootsRoute, defaultActive]"
    :default-checked-keys="[defaultActive]"
    highlight-current
    show-checkbox
    check-strictly
> 
</el-tree>

如何让el-tree树形菜单组件一次只能选中一个路由?开启  highlight-current  和  check-strictly  属性。

highlight-current:是否高亮当前选中节点,默认值是 false。
check-strictly:在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false

通过watchEffect监听路由地址变化动态更新选中节点。

// 选择节点
const handleNodeSelect = (data) => {
    // console.log(data);
    if(data.children) return

    if(utils.checkExternal(data.path)) {
        alert(data.path)
    }else {
        treeRef.value.setCheckedKeys([data.path], true)
        router.push(data.path)
    }
}

// 监听路由变化,设置选中节点
const routeChanged = async () => {
    if(!treeRef.value) return
    treeRef.value.setCheckedKeys([route.path], true)
}

watchEffect(routeChanged)

另外还需自定义选中行的颜色样式。

// 树形睬単el-tree样式
.indexlayout-treemenu {
    border: 3px dashed #f90; padding: 10px;

    // 选中行颜色
    .el-tree--highlight-current .el-tree-node.is-checked>.el-tree-node__content {
        background: $--color-primary; color: #fff;
    }
}

另外附上自定义路由JSON文件。

/**
 * @desc    主页面路由集合
 * @author  ANDY
 * -----------------------------------------
 * 路由参数说明:
 * path: \'/home\'                    链接
 * redirect: \'\'                     路径重定向
 * meta: {
 *  auth: true                      需要登录验证
 *  icon: \'home\'                    显示侧边栏图标(1、使用iconfont icon-xxx  2、使用饿了么el-icon-xxx)
 *  title: \'标题\'                    名称(显示在侧边栏/面包屑/浏览器title)
 *  breadcrumb: [                   面包屑导航
 *      {
 *          title: \'标题\'            标题
 *          path: \'/demo\'           链接
 *      }
 *  ]
 *  activeRoute: \'/home/dashboard\'  侧边栏链接选中,默认route.path
 *  rootsRoute: \'/home\'             所属顶部链接选中
 * }
 */

import emptyLayout from \'@/layouts/empty.vue\'

const mainRoutes = [
    // 首页模块
    {
        path: \'/home\',
        redirect: \'/home/dashboard\',
        component: emptyLayout,
        meta: {
            auth: true, //是否登录验证
            icon: \'icon-ding\',
            title: \'layout__main-menu__home\',
            hidden: false, //隐藏菜单项
        },
        children: [
            {
                path: \'dashboard\',
                component: () => import(\'@/views/dashboard.vue\'),
                meta: {
                    auth: true,
                    icon: \'icon-haoyou\',
                    title: \'layout__main-menu__home_dashboard\'
                }
            },
            {
                path: \'breadcrumbs\',
                component: () => import(\'@/views/breadcrumbs/index.vue\'),
                meta: {
                    auth: true,
                    icon: \'icon-down\',
                    title: \'layout__main-menu__home_breadcrumbs\',
                    // 自定义面包屑
                    breadcrumb: [
                        {
                            title: \'layout__main-menu__home_breadcrumbs\',
                            path: \'/home/breadcrumbs\'
                        },
                        {
                            title: \'layout__main-menu__home\',
                            path: \'/home\'
                        },
                        {
                            title: \'layout__main-menu__home_breadcrumbs-link\',
                            path: \'https://cn.vitejs.dev/\'
                        }
                    ]
                }
            },
            {
                path: \'https://cn.vitejs.dev/\',
                meta: {
                    // auth: true,
                    icon: \'icon-go\',
                    title: \'layout__main-menu__home_docs\',
                    rootsRoute: \'/home\'
                }
            },
            {
                path: \'tree\',
                component: () => import(\'@/views/component/tree/index.vue\'),
                meta: {
                    auth: true,
                    icon: \'el-icon-s-data\',
                    title: \'layout__main-menu__home_tree\'
                },
            },
        ]
    },


    // 组件模块
    {
        path: \'/component\',
        redirect: \'/component/table/allTable\',
        component: emptyLayout,
        meta: {
            auth: true, //是否登录验证
            icon: \'el-icon-s-operation\',
            title: \'layout__main-menu__component\',
            hidden: false, //隐藏菜单项
        },
        children: [
            {
                path: \'table\',
                redirect: \'/component/table/allTable\',
                component: emptyLayout,
                meta: {
                    auth: true,
                    icon: \'el-icon-s-grid\',
                    title: \'layout__main-menu__component_table\'
                },
                children: [
                    {
                        path: \'allTable\',
                        component: () => import(\'@/views/component/table/all.vue\'),
                        meta: {
                            title: \'layout__main-menu__component_table-all\'
                        }
                    },
                    {
                        path: \'customTable\',
                        component: () => import(\'@/views/component/table/custom.vue\'),
                        meta: {
                            title: \'layout__main-menu__component_table-custom\'
                        }
                    },
                    {
                        path: \'search\',
                        redirect: \'/component/table/search/searchlist\',
                        component: emptyLayout,
                        meta: {
                            title: \'layout__main-menu__component_table-search\'
                        },
                        children: [
                            {
                                path: \'searchlist\',
                                component: () => import(\'@/views/component/table/search.vue\'),
                                meta: {
                                    title: \'layout__main-menu__component_table-search-list\'
                                }
                            }
                        ]
                    }
                ]
            },
            {
                path: \'form\',
                redirect: \'/component/form/allForm\',
                component: emptyLayout,
                meta: {
                    auth: true,
                    icon: \'el-icon-cpu\',
                    title: \'layout__main-menu__component_form\'
                },
                children: [
                    {
                        path: \'allForm\',
                        component: () => import(\'@/views/component/form/all.vue\'),
                        meta: {
                            title: \'layout__main-menu__component_form-all\'
                        }
                    },
                    {
                        path: \'customForm\',
                        component: () => import(\'@/views/component/form/custom.vue\'),
                        meta: {
                            title: \'layout__main-menu__component_form-custom\'
                        }
                    }
                ]
            },
            {
                path: \'editor\',
                component: () => import(\'@/views/component/editor/index.vue\'),
                meta: {
                    auth: true,
                    icon: \'el-icon-cpu\',
                    title: \'layout__main-menu__component_editor\'
                },
            },
        ]
    },

    // 更多路由配置...
]

export default mainRoutes

OK,以上就是基于vite2和electron开发简易后台模板的一些分享,希望对大家有所帮助哈~~ 

分类:

技术点:

相关文章: