基于tauri+vue3+pinia2客户端管理系统程序|tauri+vite4后台系统

TauriAdmin一款跨端通用后台系统模板解决方案

基于 tauri rust webview2 整合 vite4 搭建桌面端 vue3 管理后台模板TauriVue3Admin。支持多窗口切换管理、vue-i18n多语言、动态路由权限、常用业务功能模块及动态路由缓存等功能。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

使用技术

  • 编码工具:Vscode
  • 框架技术:tauri+vite4+vue3+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

在这里插入图片描述

特色

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

在这里插入图片描述

项目搭建结构目录

在这里插入图片描述
整个项目基于tauri脚手架创建,全部采用vue3 setup语法糖编码开发。

在这里插入图片描述
tauri-admin支持创建多窗口,如上图从登录窗口切换到主窗口。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

tauri多窗口封装

在src目录下新建一个管理tauri多窗口文件夹。
在这里插入图片描述
创建新窗口配置参数

// 创建窗口参数配置
export const windowConfig = {
    
    
    label: null,            // 窗口唯一label
    title: '',              // 窗口标题
    url: '',                // 路由地址url
    width: 1000,            // 窗口宽度
    height: 640,            // 窗口高度
    minWidth: null,         // 窗口最小宽度
    minHeight: null,        // 窗口最小高度
    x: null,                // 窗口相对于屏幕左侧坐标
    y: null,                // 窗口相对于屏幕顶端坐标
    center: true,           // 窗口居中显示
    resizable: true,        // 是否支持缩放
    maximized: false,       // 最大化窗口
    decorations: false,     // 窗口是否装饰边框及导航条
    alwaysOnTop: false,     // 置顶窗口
    fileDropEnabled: false, // 禁止系统拖放
    visible: false          // 隐藏窗口
}

在这里插入图片描述

/**
 * @desc    窗口管理
 * @author: YXY  Q:282310962
 * @time    2023.07
 */

import {
    
     WebviewWindow, appWindow, getAll } from '@tauri-apps/api/window'
import {
    
     relaunch, exit } from '@tauri-apps/api/process'
import {
    
     emit, listen } from '@tauri-apps/api/event'

import {
    
     setWin } from './actions'

// 创建窗口参数配置
export const windowConfig = {
    
    
    label: null,            // 窗口唯一label
    title: '',              // 窗口标题
    url: '',                // 路由地址url
    width: 1000,            // 窗口宽度
    height: 640,            // 窗口高度
    minWidth: null,         // 窗口最小宽度
    minHeight: null,        // 窗口最小高度
    x: null,                // 窗口相对于屏幕左侧坐标
    y: null,                // 窗口相对于屏幕顶端坐标
    center: true,           // 窗口居中显示
    resizable: true,        // 是否支持缩放
    maximized: false,       // 最大化窗口
    decorations: false,     // 窗口是否装饰边框及导航条
    alwaysOnTop: false,     // 置顶窗口
    fileDropEnabled: false, // 禁止系统拖放
    visible: false          // 隐藏窗口
}

class Windows {
    
    
    constructor() {
    
    
        // 主窗口
        this.mainWin = null
    }

    // 创建新窗口
    async createWin(options) {
    
    
        console.log('-=-=-=-=-=开始创建窗口')

        const args = Object.assign({
    
    }, windowConfig, options)

        // 判断窗口是否存在
        const existWin = getAll().find(w => w.label == args.label)
        if(existWin) {
    
    
            console.log('窗口已存在>>', existWin)
            if(existWin.label.indexOf('main') == -1) {
    
    
                // 自定义处理...
            }
        }

        // 是否主窗口
        if(args.label.indexOf('main') > -1) {
    
    
            console.log('该窗口是主窗口')
            // 自定义处理...
        }

        // 创建窗口对象
        let win = new WebviewWindow(args.label, args)
        // 是否最大化
        if(args.maximized && args.resizable) {
    
    
            win.maximize()
        }

        // 窗口创建完毕/失败
        win.once('tauri://created', async() => {
    
    
            console.log('window create success!')
            await win?.show()
        })

        win.once('tauri://error', async() => {
    
    
            console.log('window create error!')
        })
    }

    // 获取窗口
    getWin(label) {
    
    
        return WebviewWindow.getByLabel(label)
    }

    // 获取全部窗口
    getAllWin() {
    
    
        return getAll()
    }

    // 开启主进程监听事件
    async listen() {
    
    
        console.log('——+——+——+——+——+开始监听窗口')

        // 创建新窗体
        await listen('win-create', (event) => {
    
    
            this.createWin(event.payload)
        })

        // 显示窗体
        await listen('win-show', async(event) => {
    
    
            if(appWindow.label.indexOf('main') == -1) return
            await appWindow.show()
            await appWindow.unminimize()
            await appWindow.setFocus()
        })

        // 隐藏窗体
        await listen('win-hide', async(event) => {
    
    
            if(appWindow.label.indexOf('main') == -1) return
            await appWindow.hide()
        })

        // 关闭窗体
        await listen('win-close', async(event) => {
    
    
            await appWindow.close()
        })

        // 退出应用
        await listen('win-exit', async(event) => {
    
    
            setWin('logout')
            await exit()
        })

        // ...
    }
}

actions.js则是处理一些渲染进程/主进程通讯。

/**
 * 处理渲染器进程到主进程的异步通信
 */

import {
    
     WebviewWindow } from '@tauri-apps/api/window'
import {
    
     emit } from '@tauri-apps/api/event'

/**
 * @desc 创建新窗口
 * @param args {object} {label: 'new', url: '/new', width: 500, height: 300, ...}
 */
export async function createWin(args) {
    
    
    console.log(args)
    await emit('win-create', args)
}

/**
 * @desc 获取窗口
 * @param args {string} 'main'|'main_login' ...
 */
export async function getWin(label) {
    
    
    return await WebviewWindow.getByLabel(label)
}

/**
 * @desc 设置窗口
 * @param type {string} 'show'|'hide'|'close'|'min'|'max'|'max2min'|'exit'|'relaunch'
 * @param id {number}
 */
export async function setWin(type) {
    
    
    await emit('win-' + type)
}

/**
 * @desc 主|渲染进程数据传递
 * @param args {object} {type: 'MSG_TYPE_XXX', value: 123}
 */
export async function setWinData(args) {
    
    
    await emit('win-setdata', args)
}

/**
 * @desc 屏蔽系统右键菜单
 */
export function disableWindowMenu() {
    
    
    document.addEventListener('contextmenu', e => e.preventDefault())
}


/**
 * @desc 登录窗口
 */
export async function loginWin() {
    
    
    await createWin({
    
    
        label: 'main_login',
        title: '登录',
        url: '/login',
        width: 520,
        height: 420,
        resizable: false,
        alwaysOnTop: true
    })
}

/**
 * @desc 主窗口
 */
export async function mainWin() {
    
    
    await createWin({
    
    
        label: 'main',
        title: 'TAURI-ADMIN',
        url: '/',
        width: 1000,
        height: 640,
        minWidth: 750,
        minHeight: 500
    })
}

在这里插入图片描述

主入口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内置了3种常用的模板。

在这里插入图片描述

<script setup>
    import {
    
     computed } from 'vue'
    import {
    
     appStore } from '@/pinia/modules/app'

    // 引入布局模板
    import Columns from './template/columns/index.vue'
    import Vertical from './template/vertical/index.vue'
    import Transverse from './template/transverse/index.vue'

    const store = appStore()
    const config = computed(() => store.config)

    const LayoutConfig = {
    
    
        columns: Columns,
        vertical: Vertical,
        transverse: Transverse
    }
</script>

<template>
    <div class="veadmin__container" :style="{'--themeSkin': store.config.skin}">
        <component :is="LayoutConfig[config.layout]" />
    </div>
</template>

路由vue-router配置

在这里插入图片描述

/**
 * 路由配置
 * @author YXY
 */

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)'
    })
    
    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配置

项目中使用的状态管理由之前的vuex替换为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

tauri vue3 国际化解决方案

tauri-admin国际化多语言采用vue-i18n插件实现功能。

在这里插入图片描述

# 安装插件
npm i vue-i18n
yarn add 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)
}

在这里插入图片描述
支持中英文/繁体切换,可自定义多语言包配置文件。

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/[email protected]",
        "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
    }
  }
}

以上就是tauri+vue3创建跨端桌面管理后台应用的一些分享,希望大家喜欢~~
如果大家觉得有些帮助,也可以关注公众号,不定期分享一些项目实例。
在这里插入图片描述

最后附上两个最新实例项目

Electron25+vue3桌面端仿制ChatGPT会话模板实例

Vue3+Tauri跨端聊天实例|tauri仿微信聊天室

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/yanxinyun1990/article/details/131734743