Tauri-Admin通用后台管理系统|tauri+vue3+pinia桌面端后台EXE
阅读原文时间:2023年07月16日阅读:2

基于tauri+vite4+pinia2跨端后台管理系统应用实例TauriAdmin

tauri-admin 基于最新跨端技术 Tauri Rust webview2 整合 Vite4 构建桌面端通用后台管理解决方案。搭载轻量级ve-plus组件库、支持多窗口切换管理、vue-i18n多语言包、动态路由权限、常用业务功能模块、3种布局模板及动态路由缓存等功能。

使用技术

  • 编码工具:vscode
  • 框架技术:tauri+vite^4.2.1+vue^3.2.45+pinia+vue-router
  • UI组件库:ve-plus (基于vue3轻量级UI组件库)
  • 样式处理:sass^1.63.6
  • 图表组件:echarts^5.4.2
  • 国际化方案:vue-i18n^9.2.2
  • 编辑器组件:wangeditor^4.7.15
  • 持久化缓存:pinia-plugin-persistedstate^3.1.0

目前tauri已经迭代到1.4,如果大家对tauri+vue3创建多窗口项目感兴趣,可以去看看之前的这篇分享文章。

https://www.cnblogs.com/xiaoyan2017/p/16812092.html

功能特性

  1. 使用跨端技术tauri1.4
  2. 最新前端技术栈vite4、vue3、pinia、vue-router、vue-i18n
  3. 支持中文/英文/繁体多语言解决方案
  4. 支持动态路由权限验证
  5. 支持路由缓存功能/tabs控制切换路由页面
  6. 内置多个模板布局风格
  7. 搭配轻量级vue3组件库veplus
  8. 高效开发,支持增删定制化页面模块

项目结构

使用tauri脚手架搭配vite4构建项目,整体采用vue3 setup语法编码开发。

主入口main.js

import { createApp } from "vue"
import "./styles.scss"
import App from "./App.vue"

// 引入路由及状态管理
import Router from './router'
import Pinia from './pinia'

// 引入插件配置
import Libs from './libs'

const app = createApp(App)

app
.use(Router)
.use(Pinia)
.use(Libs)
.mount("#app")

Tauri-Admin布局模板

提供了三种常见的布局模板,大家也可以定制喜欢的模板样式。

路由/pinia状态管理

如上图:配置router路由信息。

/**
* 路由配置
* @author YXY Q:282310962
*/

import { appWindow } from '@tauri-apps/api/window'
import { createRouter, createWebHistory } from 'vue-router'
import { appStore } from '@/pinia/modules/app'
import { hasPermission } from '@/hooks/usePermission'
import { loginWin } from '@/multiwins/actions'

// 批量导入modules路由
const modules = import.meta.glob('./modules/*.js', { eager: true })
const patchRoutes = Object.keys(modules).map(key => modules[key].default).flat()

/**
* @description 动态路由参数配置
* @param path ==> 菜单路径
* @param redirect ==> 重定向地址
* @param component ==> 视图文件路径
* 菜单信息(meta)
* @param meta.icon ==> 菜单图标
* @param meta.title ==> 菜单标题
* @param meta.activeRoute ==> 路由选中(默认空 route.path)
* @param meta.rootRoute ==> 所属根路由选中(默认空)
* @param meta.roles ==> 页面权限 ['admin', 'dev', 'test']
* @param meta.breadcrumb ==> 自定义面包屑导航 [{meta:{…}, path: '…'}]
* @param meta.isAuth ==> 是否需要验证
* @param meta.isHidden ==> 是否隐藏页面
* @param meta.isFull ==> 是否全屏页面
* @param meta.isKeepAlive ==> 是否缓存页面
* @param meta.isAffix ==> 是否固定标签(tabs标签栏不能关闭)
* */
const routes = [
// 首页
{
path: '/',
redirect: '/home'
},
// 错误模块
{
path: '/:pathMatch(.*)*',
component: () => import('@views/error/404.vue'),
meta: {
title: 'page__error-notfound'
}
},
…patchRoutes
]

const router = createRouter({
history: createWebHistory(),
routes
})

// 全局钩子拦截
router.beforeEach((to, from, next) => {
// 开启加载提示
loading({
text: 'Loading…',
background: 'rgba(70, 255, 170, .1)',
onOpen: () => {
console.log('开启loading')
},
onClose: () => {
console.log('关闭loading')
}
})

const store = appStore()  
if(to?.meta?.isAuth && !store.isLogged) {  
    loginWin()  
    loading.close()  
}else if(!hasPermission(store.roles, to?.meta?.roles)) {  
    // 路由鉴权  
    appWindow?.show()  
    next('/error/forbidden')  
    loading.close()  
    Notify({  
        title: '访问限制!',  
        description: \`<span style="color: #999;">当前登录角色 ${store.roles} 没有操作权限,请联系管理员授权后再操作。</div>\`,  
        type: 'danger',  
        icon: 've-icon-unlock',  
        time: 10  
    })  
}else {  
    appWindow?.show()  
    next()  
}  

})

router.afterEach(() => {
loading.close()
})

router.onError(error => {
loading.close()
console.warn('Router Error》》', error.message);
})

export default router

如上图:vue3项目搭配pinia进行状态管理。

/**
* 状态管理 Pinia util
* @author YXY
*/

import { createPinia } from 'pinia'
// 引入pinia本地持久化存储
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)

export default pinia

自定义路由菜单

项目中的三种模板提供了不同的路由菜单。均是基于Menu组件封装的RouteMenu菜单。

 

Menu组件支持横向/竖向排列,调用非常简单。

tauri-admin多语言配置

tauri-vue3-admin项目使用vue-i18n进行多语言处理。

import { createI18n } from 'vue-i18n'
import { appStore } from '@/pinia/modules/app'

// 引入语言配置
import enUS from './en-US'
import zhCN from './zh-CN'
import zhTW from './zh-TW'

// 默认语言
export const langVal = 'zh-CN'

export default async (app) => {
const store = appStore()
const lang = store.lang || langVal

const i18n = createI18n({  
    legacy: false,  
    locale: lang,  
    messages: {  
        'en': enUS,  
        'zh-CN': zhCN,  
        'zh-TW': zhTW  
    }  
})

app.use(i18n)  

}

路由缓存功能

项目支持配置路由页面缓存功能。可以在全局pinia/modules/app.js中配置,也可以在router配置项meta中配置isKeepAlive: true。

import { ref, nextTick } from 'vue'
import { useRoute } from 'vue-router'
import { defineStore } from 'pinia'
import { appStore } from '@/pinia/modules/app'

export const tabsStore = defineStore('tabs', () => {
const currentRoute = useRoute()
const store = appStore()

    /\*state\*/  
    const tabViews = ref(\[\]) // 标签栏列表  
    const cacheViews = ref(\[\]) // 缓存列表  
    const reload = ref(true) // 刷新标识

    // 判断tabViews某个路由是否存在  
    const tabIndex = (route) => {  
        return tabViews.value.findIndex(item => item?.path === route?.path)  
    }

    /\*actions\*/  
    // 新增标签  
    const addTabs = (route) => {  
        const index = tabIndex(route)  
        if(index > -1) {  
            tabViews.value.map(item => {  
                if(item.path == route.path) {  
                    // 当前路由缓存  
                    return Object.assign(item, route)  
                }  
            })  
        }else {  
            tabViews.value.push(route)  
        }

        // 更新keep-alive缓存  
        updateCacheViews()  
    }

    // 移除标签  
    const removeTabs = (route) => {  
        const index = tabIndex(route)  
        if(index > -1) {  
            tabViews.value.splice(index, 1)  
        }

        // 更新keep-alive缓存  
        updateCacheViews()  
    }

    // 移除左侧标签  
    const removeLeftTabs = (route) => {  
        const index = tabIndex(route)  
        if(index > -1) {  
            tabViews.value = tabViews.value.filter((item, i) => item?.meta?.isAffix || i >= index)  
        }

        // 更新keep-alive缓存  
        updateCacheViews()  
    }

    // 移除右侧标签  
    const removeRightTabs = (route) => {  
        const index = tabIndex(route)  
        if(index > -1) {  
            tabViews.value = tabViews.value.filter((item, i) => item?.meta?.isAffix || i <= index)  
        }

        // 更新keep-alive缓存  
        updateCacheViews()  
    }

    // 移除其它标签  
    const removeOtherTabs = (route) => {  
        tabViews.value = tabViews.value.filter(item => item?.meta?.isAffix || item?.path === route?.path)

        // 更新keep-alive缓存  
        updateCacheViews()  
    }

    // 移除所有标签  
    const clearTabs = () => {  
        tabViews.value = tabViews.value.filter(item => item?.meta?.isAffix)

        // 更新keep-alive缓存  
        updateCacheViews()  
    }

    // 更新keep-alive缓存  
    const updateCacheViews = () => {  
        cacheViews.value = tabViews.value.filter(item => store.config.keepAlive || item?.meta?.isKeepAlive).map(item => item.name)  
        console.log('cacheViews缓存路由>>:', cacheViews.value)  
    }

    // 移除keep-alive缓存  
    const removeCacheViews = (route) => {  
        cacheViews.value = cacheViews.value.filter(item => item !== route?.name)  
    }

    // 刷新路由  
    const reloadTabs = () => {  
        removeCacheViews(currentRoute)  
        reload.value = false  
        nextTick(() => {  
            updateCacheViews()  
            reload.value = true  
            document.documentElement.scrollTo({ left: 0, top: 0 })  
        })  
    }

    // 清空缓存  
    const clear = () => {  
        tabViews.value = \[\]  
        cacheViews.value = \[\]  
    }

    return {  
        tabViews,  
        cacheViews,  
        reload,

        addTabs,  
        removeTabs,  
        removeLeftTabs,  
        removeRightTabs,  
        removeOtherTabs,  
        clearTabs,  
        reloadTabs,  
        clear  
    }  
},  
// 本地持久化存储(默认存储localStorage)  
{  
    // persist: true  
    persist: {  
        // key: 'tabsState',  
        storage: localStorage,  
        paths: \['tabViews', 'cacheViews'\]  
    }  
}  

)

tauri.conf.json配置

{
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn build",
"devPath": "http://localhost:1420",
"distDir": "../dist",
"withGlobalTauri": false
},
"package": {
"productName": "tauri-admin",
"version": "0.0.0"
},
"tauri": {
"allowlist": {
"all": true,
"shell": {
"all": false,
"open": true
}
},
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.admin",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
},
"security": {
"csp": null
},
"windows": [
{
"fullscreen": false,
"resizable": true,
"title": "tauri-admin",
"width": 1000,
"height": 640,
"center": true,
"decorations": false,
"fileDropEnabled": false,
"visible": false
}
],
"systemTray": {
"iconPath": "icons/icon.ico",
"iconAsTemplate": true,
"menuOnLeftClick": false
}
}
}

Cargo.toml配置

[package]
name = "tauri-admin"
version = "0.0.0"
description = "基于tauri+vue3+vite4+pinia轻量级桌面端后台管理Tauri-Admin"
authors = "andy 282310962@qq.com"
license = ""
repository = ""
edition = "2023"

See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[build-dependencies]
tauri-build = { version = "1.4", features = [] }

[dependencies]
tauri = { version = "1.4", features = ["api-all", "icon-ico", "icon-png", "system-tray"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

[features]

this feature is used for production builds or when `devPath` points to the filesystem

DO NOT REMOVE!!

custom-protocol = ["tauri/custom-protocol"]

OK,基于tauri+vue3跨端后台管理系统就分享到这里。希望对大家有所帮助哈~~

最后附上两个最新开发的Electron和uniapp跨端项目实例

https://www.cnblogs.com/xiaoyan2017/p/17468074.html

https://www.cnblogs.com/xiaoyan2017/p/17507581.html

isKeepAlive: true

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器