Based on tauri+vue3+pinia2 client management system program | tauri+vite4 backend system

TauriAdmin is a cross-end universal backend system template solution

Based on tauri rust webview2 and integrating vite4 , we build the desktop vue3 management backend template TauriVue3Admin. Supports multi-window switching management, vue-i18n multi-language, dynamic routing permissions, common business function modules and dynamic routing cache and other functions.

Insert image description here
Insert image description here
Insert image description here

Use technology

  • Coding tool: Vscode
  • Frame technology: tauri+vite4+vue3+pinia+vue-router
  • UI component library: ve-plus (based on vue3 lightweight UI component library)
  • Style processing: sass^1.63.6
  • Chart component: echarts^5.4.2
  • Internationalization solution: vue-i18n^9.2.2
  • Editor component: wangeditor^4.7.15
  • Local cache: pinia-plugin-persistedstate^3.1.0

Insert image description here

feature

  1. The latest cross-end technology tauri1.4 rust webview2
  2. Front-end technology stack vite4, vue3, pinia, vue-router, vue-i18n
  3. Support Chinese/English/Traditional multi-language solutions
  4. Support dynamic routing authority verification
  5. Support route caching function/tabs control to switch routing pages
  6. Built-in multiple template layout styles
  7. Paired with lightweight vue3 component library veplus

Insert image description here

Project construction structure directory

Insert image description here
The entire project is created based on tauri scaffolding and is entirely vue3 setupdeveloped using syntax sugar coding.

Insert image description here
tauri-admin supports the creation of multiple windows, switching from the login window to the main window as shown above.

Insert image description here
Insert image description here
Insert image description here
Insert image description here
Insert image description here
Insert image description here
Insert image description here

tauri multi-window package

Create a new management tauri multi-window folder in the src directory.
Insert image description here
Create new window configuration parameters

// 创建窗口参数配置
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          // 隐藏窗口
}

Insert image description here

/**
 * @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 handles some rendering process/main process communication.

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

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
    })
}

Insert image description here

Main entrance 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")

layout template

tauri-admin has built-in 3 commonly used templates.

Insert image description here

<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>

Routing vue-router configuration

Insert image description here

/**
 * 路由配置
 * @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

Insert image description here
Insert image description here

vue3 state management pinia configuration

The state management used in the project has been replaced by pinia from the previous vuex.
Insert image description here

/**
 * 状态管理 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 internationalization solution

tauri-admin internationalization and multi-language use vue-i18nplug-ins to implement functions.

Insert image description here

# 安装插件
npm i vue-i18n
yarn add vue-i18n

Insert image description here

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)
}

Insert image description here
Supports Chinese-English/Traditional Chinese switching, and can customize multi-language package configuration files.

tauri.conf.json configuration

Configure some packaging parameters, window startup configuration parameters and tray parameters.

{
    
    
  "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
    }
  }
}

The above is some sharing of tauri+vue3's creation of cross-end desktop management background applications. I hope you like it~~
If you find it helpful, you can also follow the official account and share some project examples from time to time.
Insert image description here

Finally, two latest example projects are attached

Electron25+vue3 desktop imitation ChatGPT session template example

Vue3+Tauri cross-end chat example | tauri imitates WeChat chat room

Insert image description here

Guess you like

Origin blog.csdn.net/yanxinyun1990/article/details/131734743